diff options
Diffstat (limited to '')
-rw-r--r-- | dom/presentation/ipc/PPresentation.ipdl | 114 | ||||
-rw-r--r-- | dom/presentation/ipc/PPresentationBuilder.ipdl | 34 | ||||
-rw-r--r-- | dom/presentation/ipc/PPresentationRequest.ipdl | 22 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationBuilderChild.cpp | 172 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationBuilderChild.h | 47 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationBuilderParent.cpp | 228 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationBuilderParent.h | 52 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationChild.cpp | 173 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationChild.h | 88 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationContentSessionInfo.cpp | 98 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationContentSessionInfo.h | 62 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationIPCService.cpp | 477 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationIPCService.h | 71 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationParent.cpp | 497 | ||||
-rw-r--r-- | dom/presentation/ipc/PresentationParent.h | 133 |
15 files changed, 2268 insertions, 0 deletions
diff --git a/dom/presentation/ipc/PPresentation.ipdl b/dom/presentation/ipc/PPresentation.ipdl new file mode 100644 index 0000000000..bfaca8fb57 --- /dev/null +++ b/dom/presentation/ipc/PPresentation.ipdl @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PContent; +include protocol PPresentationRequest; +include protocol PPresentationBuilder; + +include InputStreamParams; + +include "mozilla/dom/PermissionMessageUtils.h"; + +using refcounted class nsIPrincipal from "nsIPrincipal.h"; +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; + +namespace mozilla { +namespace dom { + +struct StartSessionRequest +{ + nsString[] urls; + nsString sessionId; + nsString origin; + nsString deviceId; + uint64_t windowId; + TabId tabId; + nsIPrincipal principal; +}; + +struct SendSessionMessageRequest +{ + nsString sessionId; + uint8_t role; + nsString data; +}; + +struct CloseSessionRequest +{ + nsString sessionId; + uint8_t role; + uint8_t closedReason; +}; + +struct TerminateSessionRequest +{ + nsString sessionId; + uint8_t role; +}; + +struct ReconnectSessionRequest +{ + nsString[] urls; + nsString sessionId; + uint8_t role; +}; + +struct BuildTransportRequest +{ + nsString sessionId; + uint8_t role; +}; + +union PresentationIPCRequest +{ + StartSessionRequest; + SendSessionMessageRequest; + CloseSessionRequest; + TerminateSessionRequest; + ReconnectSessionRequest; + BuildTransportRequest; +}; + +sync protocol PPresentation +{ + manager PContent; + manages PPresentationBuilder; + manages PPresentationRequest; + +child: + async NotifyAvailableChange(nsString[] aAvailabilityUrls, + bool aAvailable); + async NotifySessionStateChange(nsString aSessionId, + uint16_t aState, + nsresult aReason); + async NotifyMessage(nsString aSessionId, nsCString aData, bool aIsBinary); + async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId); + async NotifyCloseSessionTransport(nsString aSessionId, + uint8_t aRole, + nsresult aReason); + + async PPresentationBuilder(nsString aSessionId, uint8_t aRole); + +parent: + async __delete__(); + + async RegisterAvailabilityHandler(nsString[] aAvailabilityUrls); + async UnregisterAvailabilityHandler(nsString[] aAvailabilityUrls); + + async RegisterSessionHandler(nsString aSessionId, uint8_t aRole); + async UnregisterSessionHandler(nsString aSessionId, uint8_t aRole); + + async RegisterRespondingHandler(uint64_t aWindowId); + async UnregisterRespondingHandler(uint64_t aWindowId); + + async PPresentationRequest(PresentationIPCRequest aRequest); + + async NotifyReceiverReady(nsString aSessionId, uint64_t aWindowId, bool aIsLoading); + async NotifyTransportClosed(nsString aSessionId, uint8_t aRole, nsresult aReason); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PPresentationBuilder.ipdl b/dom/presentation/ipc/PPresentationBuilder.ipdl new file mode 100644 index 0000000000..e32b02e8f3 --- /dev/null +++ b/dom/presentation/ipc/PPresentationBuilder.ipdl @@ -0,0 +1,34 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PPresentation; + +namespace mozilla { +namespace dom { + +async protocol PPresentationBuilder +{ + manager PPresentation; + +parent: + async SendOffer(nsString aSDP); + async SendAnswer(nsString aSDP); + async SendIceCandidate(nsString aCandidate); + async Close(nsresult aReason); + + async OnSessionTransport(); + async OnSessionTransportError(nsresult aReason); + +child: + async OnOffer(nsString aSDP); + async OnAnswer(nsString aSDP); + async OnIceCandidate(nsString aCandidate); + + async __delete__(); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PPresentationRequest.ipdl b/dom/presentation/ipc/PPresentationRequest.ipdl new file mode 100644 index 0000000000..fa99dfcabb --- /dev/null +++ b/dom/presentation/ipc/PPresentationRequest.ipdl @@ -0,0 +1,22 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PPresentation; + +namespace mozilla { +namespace dom { + +sync protocol PPresentationRequest +{ + manager PPresentation; + +child: + async __delete__(nsresult result); + async NotifyRequestUrlSelected(nsString aUrl); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationBuilderChild.cpp b/dom/presentation/ipc/PresentationBuilderChild.cpp new file mode 100644 index 0000000000..d4e1120392 --- /dev/null +++ b/dom/presentation/ipc/PresentationBuilderChild.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DCPresentationChannelDescription.h" +#include "nsComponentManagerUtils.h" +#include "nsGlobalWindow.h" +#include "PresentationBuilderChild.h" +#include "PresentationIPCService.h" +#include "nsServiceManagerUtils.h" +#include "mozilla/Unused.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_ISUPPORTS(PresentationBuilderChild, + nsIPresentationSessionTransportBuilderListener) + +PresentationBuilderChild::PresentationBuilderChild(const nsString& aSessionId, + uint8_t aRole) + : mSessionId(aSessionId), mRole(aRole) {} + +nsresult PresentationBuilderChild::Init() { + mBuilder = do_CreateInstance( + "@mozilla.org/presentation/datachanneltransportbuilder;1"); + if (NS_WARN_IF(!mBuilder)) { + return NS_ERROR_NOT_AVAILABLE; + } + + uint64_t windowId = 0; + + nsCOMPtr<nsIPresentationService> service = + do_GetService(PRESENTATION_SERVICE_CONTRACTID); + if (NS_WARN_IF(!service)) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (NS_WARN_IF(NS_FAILED( + service->GetWindowIdBySessionId(mSessionId, mRole, &windowId)))) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsPIDOMWindowInner* window = + nsGlobalWindowInner::GetInnerWindowWithId(windowId); + if (NS_WARN_IF(!window)) { + return NS_ERROR_NOT_AVAILABLE; + } + + return mBuilder->BuildDataChannelTransport(mRole, window, this); +} + +void PresentationBuilderChild::ActorDestroy(ActorDestroyReason aWhy) { + mBuilder = nullptr; + mActorDestroyed = true; +} + +mozilla::ipc::IPCResult PresentationBuilderChild::RecvOnOffer( + const nsString& aSDP) { + if (NS_WARN_IF(!mBuilder)) { + return IPC_FAIL_NO_REASON(this); + } + RefPtr<DCPresentationChannelDescription> description = + new DCPresentationChannelDescription(aSDP); + + if (NS_WARN_IF(NS_FAILED(mBuilder->OnOffer(description)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationBuilderChild::RecvOnAnswer( + const nsString& aSDP) { + if (NS_WARN_IF(!mBuilder)) { + return IPC_FAIL_NO_REASON(this); + } + RefPtr<DCPresentationChannelDescription> description = + new DCPresentationChannelDescription(aSDP); + + if (NS_WARN_IF(NS_FAILED(mBuilder->OnAnswer(description)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationBuilderChild::RecvOnIceCandidate( + const nsString& aCandidate) { + if (NS_WARN_IF(mBuilder && NS_FAILED(mBuilder->OnIceCandidate(aCandidate)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +// nsPresentationSessionTransportBuilderListener +NS_IMETHODIMP +PresentationBuilderChild::OnSessionTransport( + nsIPresentationSessionTransport* aTransport) { + if (NS_WARN_IF(mActorDestroyed || !SendOnSessionTransport())) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIPresentationService> service = + do_GetService(PRESENTATION_SERVICE_CONTRACTID); + NS_WARNING_ASSERTION(service, "no presentation service"); + if (service) { + Unused << NS_WARN_IF( + NS_FAILED(static_cast<PresentationIPCService*>(service.get()) + ->NotifySessionTransport(mSessionId, mRole, aTransport))); + } + mBuilder = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderChild::OnError(nsresult reason) { + mBuilder = nullptr; + + if (NS_WARN_IF(mActorDestroyed || !SendOnSessionTransportError(reason))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderChild::SendOffer(nsIPresentationChannelDescription* aOffer) { + nsAutoString SDP; + nsresult rv = aOffer->GetDataChannelSDP(SDP); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(mActorDestroyed || !SendSendOffer(SDP))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderChild::SendAnswer( + nsIPresentationChannelDescription* aAnswer) { + nsAutoString SDP; + nsresult rv = aAnswer->GetDataChannelSDP(SDP); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(mActorDestroyed || !SendSendAnswer(SDP))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderChild::SendIceCandidate(const nsAString& candidate) { + if (NS_WARN_IF(mActorDestroyed || + !SendSendIceCandidate(nsString(candidate)))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderChild::Close(nsresult reason) { + if (NS_WARN_IF(mActorDestroyed || !SendClose(reason))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationBuilderChild.h b/dom/presentation/ipc/PresentationBuilderChild.h new file mode 100644 index 0000000000..8da43245da --- /dev/null +++ b/dom/presentation/ipc/PresentationBuilderChild.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PresentationBuilderChild_h +#define mozilla_dom_PresentationBuilderChild_h + +#include "mozilla/dom/PPresentationBuilderChild.h" +#include "nsIPresentationSessionTransportBuilder.h" + +namespace mozilla { +namespace dom { + +class PresentationBuilderChild final + : public PPresentationBuilderChild, + public nsIPresentationSessionTransportBuilderListener { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER + + explicit PresentationBuilderChild(const nsString& aSessionId, uint8_t aRole); + + nsresult Init(); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult RecvOnOffer(const nsString& aSDP); + + mozilla::ipc::IPCResult RecvOnAnswer(const nsString& aSDP); + + mozilla::ipc::IPCResult RecvOnIceCandidate(const nsString& aCandidate); + + private: + virtual ~PresentationBuilderChild() = default; + + nsString mSessionId; + uint8_t mRole; + bool mActorDestroyed = false; + nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> mBuilder; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PresentationBuilderChild_h diff --git a/dom/presentation/ipc/PresentationBuilderParent.cpp b/dom/presentation/ipc/PresentationBuilderParent.cpp new file mode 100644 index 0000000000..cc451d7465 --- /dev/null +++ b/dom/presentation/ipc/PresentationBuilderParent.cpp @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DCPresentationChannelDescription.h" +#include "PresentationBuilderParent.h" +#include "PresentationSessionInfo.h" + +namespace mozilla { +namespace dom { + +namespace { + +class PresentationSessionTransportIPC final + : public nsIPresentationSessionTransport { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT + + PresentationSessionTransportIPC(PresentationParent* aParent, + const nsAString& aSessionId, uint8_t aRole) + : mParent(aParent), mSessionId(aSessionId), mRole(aRole) { + MOZ_ASSERT(mParent); + } + + private: + virtual ~PresentationSessionTransportIPC() = default; + + RefPtr<PresentationParent> mParent; + nsString mSessionId; + uint8_t mRole; +}; + +NS_IMPL_ISUPPORTS(PresentationSessionTransportIPC, + nsIPresentationSessionTransport) + +NS_IMETHODIMP +PresentationSessionTransportIPC::GetCallback( + nsIPresentationSessionTransportCallback** aCallback) { + return NS_OK; +} + +NS_IMETHODIMP +PresentationSessionTransportIPC::SetCallback( + nsIPresentationSessionTransportCallback* aCallback) { + if (aCallback) { + aCallback->NotifyTransportReady(); + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationSessionTransportIPC::GetSelfAddress(nsINetAddr** aSelfAddress) { + MOZ_ASSERT(false, "Not expected."); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +PresentationSessionTransportIPC::EnableDataNotification() { return NS_OK; } + +NS_IMETHODIMP +PresentationSessionTransportIPC::Send(const nsAString& aData) { return NS_OK; } + +NS_IMETHODIMP +PresentationSessionTransportIPC::SendBinaryMsg(const nsACString& aData) { + return NS_OK; +} + +NS_IMETHODIMP +PresentationSessionTransportIPC::SendBlob(Blob* aBlob) { return NS_OK; } + +NS_IMETHODIMP +PresentationSessionTransportIPC::Close(nsresult aReason) { + if (NS_WARN_IF(!mParent->SendNotifyCloseSessionTransport(mSessionId, mRole, + aReason))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // anonymous namespace + +NS_IMPL_ISUPPORTS(PresentationBuilderParent, + nsIPresentationSessionTransportBuilder, + nsIPresentationDataChannelSessionTransportBuilder) + +PresentationBuilderParent::PresentationBuilderParent( + PresentationParent* aParent) + : mParent(aParent) {} + +PresentationBuilderParent::~PresentationBuilderParent() { + if (mNeedDestroyActor) { + Unused << NS_WARN_IF(!Send__delete__(this)); + } +} + +NS_IMETHODIMP +PresentationBuilderParent::BuildDataChannelTransport( + uint8_t aRole, mozIDOMWindow* aWindow, /* unused */ + nsIPresentationSessionTransportBuilderListener* aListener) { + mBuilderListener = aListener; + + RefPtr<PresentationSessionInfo> info = + static_cast<PresentationSessionInfo*>(aListener); + nsAutoString sessionId(info->GetSessionId()); + if (NS_WARN_IF(!mParent->SendPPresentationBuilderConstructor(this, sessionId, + aRole))) { + return NS_ERROR_FAILURE; + } + mIPCSessionTransport = + new PresentationSessionTransportIPC(mParent, sessionId, aRole); + mNeedDestroyActor = true; + mParent = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderParent::OnIceCandidate(const nsAString& aCandidate) { + if (NS_WARN_IF(!SendOnIceCandidate(nsString(aCandidate)))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderParent::OnOffer( + nsIPresentationChannelDescription* aDescription) { + nsAutoString SDP; + nsresult rv = aDescription->GetDataChannelSDP(SDP); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!SendOnOffer(SDP))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderParent::OnAnswer( + nsIPresentationChannelDescription* aDescription) { + nsAutoString SDP; + nsresult rv = aDescription->GetDataChannelSDP(SDP); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!SendOnAnswer(SDP))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationBuilderParent::NotifyDisconnected(nsresult aReason) { + return NS_OK; +} + +void PresentationBuilderParent::ActorDestroy(ActorDestroyReason aWhy) { + mNeedDestroyActor = false; + mParent = nullptr; + mBuilderListener = nullptr; +} + +mozilla::ipc::IPCResult PresentationBuilderParent::RecvSendOffer( + const nsString& aSDP) { + RefPtr<DCPresentationChannelDescription> description = + new DCPresentationChannelDescription(aSDP); + if (NS_WARN_IF(!mBuilderListener || + NS_FAILED(mBuilderListener->SendOffer(description)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationBuilderParent::RecvSendAnswer( + const nsString& aSDP) { + RefPtr<DCPresentationChannelDescription> description = + new DCPresentationChannelDescription(aSDP); + if (NS_WARN_IF(!mBuilderListener || + NS_FAILED(mBuilderListener->SendAnswer(description)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationBuilderParent::RecvSendIceCandidate( + const nsString& aCandidate) { + if (NS_WARN_IF(!mBuilderListener || + NS_FAILED(mBuilderListener->SendIceCandidate(aCandidate)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationBuilderParent::RecvClose( + const nsresult& aReason) { + if (NS_WARN_IF(!mBuilderListener || + NS_FAILED(mBuilderListener->Close(aReason)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +// Delegate to nsIPresentationSessionTransportBuilderListener +mozilla::ipc::IPCResult PresentationBuilderParent::RecvOnSessionTransport() { + RefPtr<PresentationBuilderParent> kungFuDeathGrip = this; + Unused << NS_WARN_IF( + !mBuilderListener || + NS_FAILED(mBuilderListener->OnSessionTransport(mIPCSessionTransport))); + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationBuilderParent::RecvOnSessionTransportError( + const nsresult& aReason) { + if (NS_WARN_IF(!mBuilderListener || + NS_FAILED(mBuilderListener->OnError(aReason)))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationBuilderParent.h b/dom/presentation/ipc/PresentationBuilderParent.h new file mode 100644 index 0000000000..6f8e097c16 --- /dev/null +++ b/dom/presentation/ipc/PresentationBuilderParent.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PresentationBuilderParent_h__ +#define mozilla_dom_PresentationBuilderParent_h__ + +#include "mozilla/dom/PPresentationBuilderParent.h" +#include "PresentationParent.h" +#include "nsIPresentationSessionTransportBuilder.h" + +namespace mozilla { +namespace dom { + +class PresentationBuilderParent final + : public PPresentationBuilderParent, + public nsIPresentationDataChannelSessionTransportBuilder { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDER + NS_DECL_NSIPRESENTATIONDATACHANNELSESSIONTRANSPORTBUILDER + + explicit PresentationBuilderParent(PresentationParent* aParent); + + mozilla::ipc::IPCResult RecvSendOffer(const nsString& aSDP); + + mozilla::ipc::IPCResult RecvSendAnswer(const nsString& aSDP); + + mozilla::ipc::IPCResult RecvSendIceCandidate(const nsString& aCandidate); + + mozilla::ipc::IPCResult RecvClose(const nsresult& aReason); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult RecvOnSessionTransport(); + + mozilla::ipc::IPCResult RecvOnSessionTransportError(const nsresult& aReason); + + private: + virtual ~PresentationBuilderParent(); + bool mNeedDestroyActor = false; + RefPtr<PresentationParent> mParent; + nsCOMPtr<nsIPresentationSessionTransportBuilderListener> mBuilderListener; + nsCOMPtr<nsIPresentationSessionTransport> mIPCSessionTransport; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PresentationBuilderParent_h__ diff --git a/dom/presentation/ipc/PresentationChild.cpp b/dom/presentation/ipc/PresentationChild.cpp new file mode 100644 index 0000000000..77737e2e5e --- /dev/null +++ b/dom/presentation/ipc/PresentationChild.cpp @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DCPresentationChannelDescription.h" +#include "mozilla/StaticPtr.h" +#include "PresentationBuilderChild.h" +#include "PresentationChild.h" +#include "PresentationIPCService.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace dom { + +/* + * Implementation of PresentationChild + */ + +PresentationChild::PresentationChild(PresentationIPCService* aService) + : mActorDestroyed(false), mService(aService) { + MOZ_ASSERT(mService); + + MOZ_COUNT_CTOR(PresentationChild); +} + +PresentationChild::~PresentationChild() { + MOZ_COUNT_DTOR(PresentationChild); + + if (!mActorDestroyed) { + Send__delete__(this); + } + mService = nullptr; +} + +void PresentationChild::ActorDestroy(ActorDestroyReason aWhy) { + mActorDestroyed = true; + mService->NotifyPresentationChildDestroyed(); + mService = nullptr; +} + +PPresentationRequestChild* PresentationChild::AllocPPresentationRequestChild( + const PresentationIPCRequest& aRequest) { + MOZ_ASSERT_UNREACHABLE( + "We should never be manually allocating " + "PPresentationRequestChild actors"); + return nullptr; +} + +bool PresentationChild::DeallocPPresentationRequestChild( + PPresentationRequestChild* aActor) { + delete aActor; + return true; +} + +mozilla::ipc::IPCResult PresentationChild::RecvPPresentationBuilderConstructor( + PPresentationBuilderChild* aActor, const nsString& aSessionId, + const uint8_t& aRole) { + // Child will build the session transport + PresentationBuilderChild* actor = + static_cast<PresentationBuilderChild*>(aActor); + if (NS_WARN_IF(NS_FAILED(actor->Init()))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +PPresentationBuilderChild* PresentationChild::AllocPPresentationBuilderChild( + const nsString& aSessionId, const uint8_t& aRole) { + RefPtr<PresentationBuilderChild> actor = + new PresentationBuilderChild(aSessionId, aRole); + + return actor.forget().take(); +} + +bool PresentationChild::DeallocPPresentationBuilderChild( + PPresentationBuilderChild* aActor) { + RefPtr<PresentationBuilderChild> actor = + dont_AddRef(static_cast<PresentationBuilderChild*>(aActor)); + return true; +} + +mozilla::ipc::IPCResult PresentationChild::RecvNotifyAvailableChange( + nsTArray<nsString>&& aAvailabilityUrls, const bool& aAvailable) { + if (mService) { + Unused << NS_WARN_IF(NS_FAILED( + mService->NotifyAvailableChange(aAvailabilityUrls, aAvailable))); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationChild::RecvNotifySessionStateChange( + const nsString& aSessionId, const uint16_t& aState, + const nsresult& aReason) { + if (mService) { + Unused << NS_WARN_IF(NS_FAILED( + mService->NotifySessionStateChange(aSessionId, aState, aReason))); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationChild::RecvNotifyMessage( + const nsString& aSessionId, const nsCString& aData, const bool& aIsBinary) { + if (mService) { + Unused << NS_WARN_IF( + NS_FAILED(mService->NotifyMessage(aSessionId, aData, aIsBinary))); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationChild::RecvNotifySessionConnect( + const uint64_t& aWindowId, const nsString& aSessionId) { + if (mService) { + Unused << NS_WARN_IF( + NS_FAILED(mService->NotifySessionConnect(aWindowId, aSessionId))); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationChild::RecvNotifyCloseSessionTransport( + const nsString& aSessionId, const uint8_t& aRole, const nsresult& aReason) { + if (mService) { + Unused << NS_WARN_IF(NS_FAILED( + mService->CloseContentSessionTransport(aSessionId, aRole, aReason))); + } + return IPC_OK(); +} + +/* + * Implementation of PresentationRequestChild + */ + +PresentationRequestChild::PresentationRequestChild( + nsIPresentationServiceCallback* aCallback) + : mActorDestroyed(false), mCallback(aCallback) { + MOZ_COUNT_CTOR(PresentationRequestChild); +} + +PresentationRequestChild::~PresentationRequestChild() { + MOZ_COUNT_DTOR(PresentationRequestChild); + + mCallback = nullptr; +} + +void PresentationRequestChild::ActorDestroy(ActorDestroyReason aWhy) { + mActorDestroyed = true; + mCallback = nullptr; +} + +mozilla::ipc::IPCResult PresentationRequestChild::Recv__delete__( + const nsresult& aResult) { + if (mActorDestroyed) { + return IPC_OK(); + } + + if (mCallback) { + if (NS_FAILED(aResult)) { + Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyError(aResult))); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationRequestChild::RecvNotifyRequestUrlSelected( + const nsString& aUrl) { + Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess(aUrl))); + return IPC_OK(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationChild.h b/dom/presentation/ipc/PresentationChild.h new file mode 100644 index 0000000000..3c02353350 --- /dev/null +++ b/dom/presentation/ipc/PresentationChild.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PresentationChild_h +#define mozilla_dom_PresentationChild_h + +#include "mozilla/dom/PPresentationBuilderChild.h" +#include "mozilla/dom/PPresentationChild.h" +#include "mozilla/dom/PPresentationRequestChild.h" + +class nsIPresentationServiceCallback; + +namespace mozilla { +namespace dom { + +class PresentationIPCService; + +class PresentationChild final : public PPresentationChild { + public: + explicit PresentationChild(PresentationIPCService* aService); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + PPresentationRequestChild* AllocPPresentationRequestChild( + const PresentationIPCRequest& aRequest); + + bool DeallocPPresentationRequestChild(PPresentationRequestChild* aActor); + + mozilla::ipc::IPCResult RecvPPresentationBuilderConstructor( + PPresentationBuilderChild* aActor, const nsString& aSessionId, + const uint8_t& aRole) override; + + PPresentationBuilderChild* AllocPPresentationBuilderChild( + const nsString& aSessionId, const uint8_t& aRole); + + bool DeallocPPresentationBuilderChild(PPresentationBuilderChild* aActor); + + mozilla::ipc::IPCResult RecvNotifyAvailableChange( + nsTArray<nsString>&& aAvailabilityUrls, const bool& aAvailable); + + mozilla::ipc::IPCResult RecvNotifySessionStateChange( + const nsString& aSessionId, const uint16_t& aState, + const nsresult& aReason); + + mozilla::ipc::IPCResult RecvNotifyMessage(const nsString& aSessionId, + const nsCString& aData, + const bool& aIsBinary); + + mozilla::ipc::IPCResult RecvNotifySessionConnect(const uint64_t& aWindowId, + const nsString& aSessionId); + + mozilla::ipc::IPCResult RecvNotifyCloseSessionTransport( + const nsString& aSessionId, const uint8_t& aRole, + const nsresult& aReason); + + private: + virtual ~PresentationChild(); + + bool mActorDestroyed = false; + RefPtr<PresentationIPCService> mService; +}; + +class PresentationRequestChild final : public PPresentationRequestChild { + friend class PresentationChild; + + public: + explicit PresentationRequestChild(nsIPresentationServiceCallback* aCallback); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult Recv__delete__(const nsresult& aResult); + + mozilla::ipc::IPCResult RecvNotifyRequestUrlSelected(const nsString& aUrl); + + private: + virtual ~PresentationRequestChild(); + + bool mActorDestroyed = false; + nsCOMPtr<nsIPresentationServiceCallback> mCallback; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PresentationChild_h diff --git a/dom/presentation/ipc/PresentationContentSessionInfo.cpp b/dom/presentation/ipc/PresentationContentSessionInfo.cpp new file mode 100644 index 0000000000..8190aedbf3 --- /dev/null +++ b/dom/presentation/ipc/PresentationContentSessionInfo.cpp @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsServiceManagerUtils.h" +#include "PresentationContentSessionInfo.h" +#include "PresentationIPCService.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_ISUPPORTS(PresentationContentSessionInfo, + nsIPresentationSessionTransportCallback); + +nsresult PresentationContentSessionInfo::Init() { + if (NS_WARN_IF(NS_FAILED(mTransport->SetCallback(this)))) { + return NS_ERROR_NOT_AVAILABLE; + } + if (NS_WARN_IF(NS_FAILED(mTransport->EnableDataNotification()))) { + return NS_ERROR_NOT_AVAILABLE; + } + return NS_OK; +} + +nsresult PresentationContentSessionInfo::Send(const nsAString& aData) { + if (!mTransport) { + return NS_ERROR_NOT_AVAILABLE; + } + + return mTransport->Send(aData); +} + +nsresult PresentationContentSessionInfo::SendBinaryMsg( + const nsACString& aData) { + if (NS_WARN_IF(!mTransport)) { + return NS_ERROR_NOT_AVAILABLE; + } + + return mTransport->SendBinaryMsg(aData); +} + +nsresult PresentationContentSessionInfo::SendBlob(Blob* aBlob) { + if (NS_WARN_IF(!mTransport)) { + return NS_ERROR_NOT_AVAILABLE; + } + + return mTransport->SendBlob(aBlob); +} + +nsresult PresentationContentSessionInfo::Close(nsresult aReason) { + if (!mTransport) { + return NS_ERROR_NOT_AVAILABLE; + } + + return mTransport->Close(aReason); +} + +// nsIPresentationSessionTransportCallback +NS_IMETHODIMP +PresentationContentSessionInfo::NotifyTransportReady() { + // do nothing since |onSessionTransport| implies this + return NS_OK; +} + +NS_IMETHODIMP +PresentationContentSessionInfo::NotifyTransportClosed(nsresult aReason) { + MOZ_ASSERT(NS_IsMainThread()); + + // Nullify |mTransport| here so it won't try to re-close |mTransport| in + // potential subsequent |Shutdown| calls. + mTransport = nullptr; + nsCOMPtr<nsIPresentationService> service = + do_GetService(PRESENTATION_SERVICE_CONTRACTID); + if (NS_WARN_IF(!service)) { + return NS_ERROR_NOT_AVAILABLE; + } + return static_cast<PresentationIPCService*>(service.get()) + ->NotifyTransportClosed(mSessionId, mRole, aReason); +} + +NS_IMETHODIMP +PresentationContentSessionInfo::NotifyData(const nsACString& aData, + bool aIsBinary) { + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr<nsIPresentationService> service = + do_GetService(PRESENTATION_SERVICE_CONTRACTID); + if (NS_WARN_IF(!service)) { + return NS_ERROR_NOT_AVAILABLE; + } + return static_cast<PresentationIPCService*>(service.get()) + ->NotifyMessage(mSessionId, aData, aIsBinary); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationContentSessionInfo.h b/dom/presentation/ipc/PresentationContentSessionInfo.h new file mode 100644 index 0000000000..7f15da1859 --- /dev/null +++ b/dom/presentation/ipc/PresentationContentSessionInfo.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PresentationContentSessionInfo_h +#define mozilla_dom_PresentationContentSessionInfo_h + +#include "nsCOMPtr.h" +#include "nsIPresentationSessionTransport.h" + +// XXX Avoid including this here by moving function bodies to the cpp file +#include "nsIPresentationService.h" +#include "nsXULAppAPI.h" + +namespace mozilla { +namespace dom { + +/** + * PresentationContentSessionInfo manages nsIPresentationSessionTransport and + * delegates the callbacks to PresentationIPCService. Only lives in content + * process. + */ +class PresentationContentSessionInfo final + : public nsIPresentationSessionTransportCallback { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK + + PresentationContentSessionInfo(const nsAString& aSessionId, uint8_t aRole, + nsIPresentationSessionTransport* aTransport) + : mSessionId(aSessionId), mRole(aRole), mTransport(aTransport) { + MOZ_ASSERT(XRE_IsContentProcess()); + MOZ_ASSERT(!aSessionId.IsEmpty()); + MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || + aRole == nsIPresentationService::ROLE_RECEIVER); + MOZ_ASSERT(aTransport); + } + + nsresult Init(); + + nsresult Send(const nsAString& aData); + + nsresult SendBinaryMsg(const nsACString& aData); + + nsresult SendBlob(Blob* aBlob); + + nsresult Close(nsresult aReason); + + private: + virtual ~PresentationContentSessionInfo() = default; + + nsString mSessionId; + uint8_t mRole; + nsCOMPtr<nsIPresentationSessionTransport> mTransport; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PresentationContentSessionInfo_h diff --git a/dom/presentation/ipc/PresentationIPCService.cpp b/dom/presentation/ipc/PresentationIPCService.cpp new file mode 100644 index 0000000000..c99ce1c2ae --- /dev/null +++ b/dom/presentation/ipc/PresentationIPCService.cpp @@ -0,0 +1,477 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/PPresentation.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/URIUtils.h" +#include "nsGlobalWindow.h" +#include "nsIPresentationListener.h" +#include "PresentationCallbacks.h" +#include "PresentationChild.h" +#include "PresentationContentSessionInfo.h" +#include "PresentationIPCService.h" +#include "PresentationLog.h" + +namespace mozilla { +namespace dom { + +namespace { + +PresentationChild* sPresentationChild; + +} // namespace + +NS_IMPL_ISUPPORTS(PresentationIPCService, nsIPresentationService, + nsIPresentationAvailabilityListener) + +PresentationIPCService::PresentationIPCService() { + ContentChild* contentChild = ContentChild::GetSingleton(); + if (NS_WARN_IF(!contentChild || contentChild->IsShuttingDown())) { + return; + } + sPresentationChild = new PresentationChild(this); + Unused << NS_WARN_IF( + !contentChild->SendPPresentationConstructor(sPresentationChild)); +} + +/* virtual */ +PresentationIPCService::~PresentationIPCService() { + Shutdown(); + + mSessionListeners.Clear(); + mSessionInfoAtController.Clear(); + mSessionInfoAtReceiver.Clear(); + sPresentationChild = nullptr; +} + +NS_IMETHODIMP +PresentationIPCService::StartSession( + const nsTArray<nsString>& aUrls, const nsAString& aSessionId, + const nsAString& aOrigin, const nsAString& aDeviceId, uint64_t aWindowId, + EventTarget* aEventTarget, nsIPrincipal* aPrincipal, + nsIPresentationServiceCallback* aCallback, + nsIPresentationTransportBuilderConstructor* aBuilderConstructor) { + if (aWindowId != 0) { + AddRespondingSessionId(aWindowId, aSessionId, + nsIPresentationService::ROLE_CONTROLLER); + } + + nsPIDOMWindowInner* window = + nsGlobalWindowInner::GetInnerWindowWithId(aWindowId); + TabId tabId = BrowserParent::GetTabIdFrom(window->GetDocShell()); + + SendRequest( + aCallback, + StartSessionRequest(aUrls, nsString(aSessionId), nsString(aOrigin), + nsString(aDeviceId), aWindowId, tabId, aPrincipal)); + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::SendSessionMessage(const nsAString& aSessionId, + uint8_t aRole, + const nsAString& aData) { + MOZ_ASSERT(!aSessionId.IsEmpty()); + MOZ_ASSERT(!aData.IsEmpty()); + + RefPtr<PresentationContentSessionInfo> info = + GetSessionInfo(aSessionId, aRole); + // data channel session transport is maintained by content process + if (info) { + return info->Send(aData); + } + + SendRequest(nullptr, SendSessionMessageRequest(nsString(aSessionId), aRole, + nsString(aData))); + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::SendSessionBinaryMsg(const nsAString& aSessionId, + uint8_t aRole, + const nsACString& aData) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aData.IsEmpty()); + MOZ_ASSERT(!aSessionId.IsEmpty()); + MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || + aRole == nsIPresentationService::ROLE_RECEIVER); + + RefPtr<PresentationContentSessionInfo> info = + GetSessionInfo(aSessionId, aRole); + // data channel session transport is maintained by content process + if (info) { + return info->SendBinaryMsg(aData); + } + + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +PresentationIPCService::SendSessionBlob(const nsAString& aSessionId, + uint8_t aRole, Blob* aBlob) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aSessionId.IsEmpty()); + MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || + aRole == nsIPresentationService::ROLE_RECEIVER); + MOZ_ASSERT(aBlob); + + RefPtr<PresentationContentSessionInfo> info = + GetSessionInfo(aSessionId, aRole); + // data channel session transport is maintained by content process + if (info) { + return info->SendBlob(aBlob); + } + + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +PresentationIPCService::CloseSession(const nsAString& aSessionId, uint8_t aRole, + uint8_t aClosedReason) { + MOZ_ASSERT(!aSessionId.IsEmpty()); + + SendRequest(nullptr, + CloseSessionRequest(nsString(aSessionId), aRole, aClosedReason)); + + RefPtr<PresentationContentSessionInfo> info = + GetSessionInfo(aSessionId, aRole); + if (info) { + return info->Close(NS_OK); + } + + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::TerminateSession(const nsAString& aSessionId, + uint8_t aRole) { + MOZ_ASSERT(!aSessionId.IsEmpty()); + + SendRequest(nullptr, TerminateSessionRequest(nsString(aSessionId), aRole)); + + RefPtr<PresentationContentSessionInfo> info = + GetSessionInfo(aSessionId, aRole); + if (info) { + return info->Close(NS_OK); + } + + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::ReconnectSession( + const nsTArray<nsString>& aUrls, const nsAString& aSessionId, uint8_t aRole, + nsIPresentationServiceCallback* aCallback) { + MOZ_ASSERT(!aSessionId.IsEmpty()); + + if (aRole != nsIPresentationService::ROLE_CONTROLLER) { + MOZ_ASSERT(false, "Only controller can call ReconnectSession."); + return NS_ERROR_INVALID_ARG; + } + + SendRequest(aCallback, + ReconnectSessionRequest(aUrls, nsString(aSessionId), aRole)); + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::BuildTransport(const nsAString& aSessionId, + uint8_t aRole) { + MOZ_ASSERT(!aSessionId.IsEmpty()); + + if (aRole != nsIPresentationService::ROLE_CONTROLLER) { + MOZ_ASSERT(false, "Only controller can call ReconnectSession."); + return NS_ERROR_INVALID_ARG; + } + + SendRequest(nullptr, BuildTransportRequest(nsString(aSessionId), aRole)); + return NS_OK; +} + +void PresentationIPCService::SendRequest( + nsIPresentationServiceCallback* aCallback, + const PresentationIPCRequest& aRequest) { + if (sPresentationChild) { + PresentationRequestChild* actor = new PresentationRequestChild(aCallback); + Unused << NS_WARN_IF( + !sPresentationChild->SendPPresentationRequestConstructor(actor, + aRequest)); + } +} + +NS_IMETHODIMP +PresentationIPCService::RegisterAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aAvailabilityUrls.IsEmpty()); + MOZ_ASSERT(aListener); + + nsTArray<nsString> addedUrls; + mAvailabilityManager.AddAvailabilityListener(aAvailabilityUrls, aListener, + addedUrls); + + if (sPresentationChild && !addedUrls.IsEmpty()) { + Unused << NS_WARN_IF( + !sPresentationChild->SendRegisterAvailabilityHandler(addedUrls)); + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::UnregisterAvailabilityListener( + const nsTArray<nsString>& aAvailabilityUrls, + nsIPresentationAvailabilityListener* aListener) { + MOZ_ASSERT(NS_IsMainThread()); + + nsTArray<nsString> removedUrls; + mAvailabilityManager.RemoveAvailabilityListener(aAvailabilityUrls, aListener, + removedUrls); + + if (sPresentationChild && !removedUrls.IsEmpty()) { + Unused << NS_WARN_IF( + !sPresentationChild->SendUnregisterAvailabilityHandler(removedUrls)); + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::RegisterSessionListener( + const nsAString& aSessionId, uint8_t aRole, + nsIPresentationSessionListener* aListener) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aListener); + + nsCOMPtr<nsIPresentationSessionListener> listener; + if (mSessionListeners.Get(aSessionId, getter_AddRefs(listener))) { + mSessionListeners.Put(aSessionId, RefPtr{aListener}); + return NS_OK; + } + + mSessionListeners.Put(aSessionId, RefPtr{aListener}); + if (sPresentationChild) { + Unused << NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler( + nsString(aSessionId), aRole)); + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId, + uint8_t aRole) { + MOZ_ASSERT(NS_IsMainThread()); + + UntrackSessionInfo(aSessionId, aRole); + + mSessionListeners.Remove(aSessionId); + if (sPresentationChild) { + Unused << NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler( + nsString(aSessionId), aRole)); + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::RegisterRespondingListener( + uint64_t aWindowId, nsIPresentationRespondingListener* aListener) { + MOZ_ASSERT(NS_IsMainThread()); + + mRespondingListeners.Put(aWindowId, RefPtr{aListener}); + if (sPresentationChild) { + Unused << NS_WARN_IF( + !sPresentationChild->SendRegisterRespondingHandler(aWindowId)); + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::UnregisterRespondingListener(uint64_t aWindowId) { + MOZ_ASSERT(NS_IsMainThread()); + + mRespondingListeners.Remove(aWindowId); + if (sPresentationChild) { + Unused << NS_WARN_IF( + !sPresentationChild->SendUnregisterRespondingHandler(aWindowId)); + } + return NS_OK; +} + +nsresult PresentationIPCService::NotifySessionTransport( + const nsString& aSessionId, const uint8_t& aRole, + nsIPresentationSessionTransport* aTransport) { + RefPtr<PresentationContentSessionInfo> info = + new PresentationContentSessionInfo(aSessionId, aRole, aTransport); + + if (NS_WARN_IF(NS_FAILED(info->Init()))) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (aRole == nsIPresentationService::ROLE_CONTROLLER) { + mSessionInfoAtController.Put(aSessionId, std::move(info)); + } else { + mSessionInfoAtReceiver.Put(aSessionId, std::move(info)); + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::GetWindowIdBySessionId(const nsAString& aSessionId, + uint8_t aRole, + uint64_t* aWindowId) { + return GetWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId); +} + +NS_IMETHODIMP +PresentationIPCService::UpdateWindowIdBySessionId(const nsAString& aSessionId, + uint8_t aRole, + const uint64_t aWindowId) { + UpdateWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId); + return NS_OK; +} + +nsresult PresentationIPCService::NotifySessionStateChange( + const nsAString& aSessionId, uint16_t aState, nsresult aReason) { + nsCOMPtr<nsIPresentationSessionListener> listener; + if (NS_WARN_IF( + !mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) { + return NS_OK; + } + + return listener->NotifyStateChange(aSessionId, aState, aReason); +} + +// Only used for OOP RTCDataChannel session transport case. +nsresult PresentationIPCService::NotifyMessage(const nsAString& aSessionId, + const nsACString& aData, + const bool& aIsBinary) { + nsCOMPtr<nsIPresentationSessionListener> listener; + if (NS_WARN_IF( + !mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) { + return NS_OK; + } + + return listener->NotifyMessage(aSessionId, aData, aIsBinary); +} + +// Only used for OOP RTCDataChannel session transport case. +nsresult PresentationIPCService::NotifyTransportClosed( + const nsAString& aSessionId, uint8_t aRole, nsresult aReason) { + RefPtr<PresentationContentSessionInfo> info = + GetSessionInfo(aSessionId, aRole); + if (NS_WARN_IF(!info)) { + return NS_ERROR_NOT_AVAILABLE; + } + Unused << NS_WARN_IF(!sPresentationChild->SendNotifyTransportClosed( + nsString(aSessionId), aRole, aReason)); + return NS_OK; +} + +nsresult PresentationIPCService::NotifySessionConnect( + uint64_t aWindowId, const nsAString& aSessionId) { + nsCOMPtr<nsIPresentationRespondingListener> listener; + if (NS_WARN_IF( + !mRespondingListeners.Get(aWindowId, getter_AddRefs(listener)))) { + return NS_OK; + } + + return listener->NotifySessionConnect(aWindowId, aSessionId); +} + +NS_IMETHODIMP +PresentationIPCService::NotifyAvailableChange( + const nsTArray<nsString>& aAvailabilityUrls, bool aAvailable) { + mAvailabilityManager.DoNotifyAvailableChange(aAvailabilityUrls, aAvailable); + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::NotifyReceiverReady( + const nsAString& aSessionId, uint64_t aWindowId, bool aIsLoading, + nsIPresentationTransportBuilderConstructor* aBuilderConstructor) { + MOZ_ASSERT(NS_IsMainThread()); + + // No actual window uses 0 as its ID. + if (NS_WARN_IF(aWindowId == 0)) { + return NS_ERROR_NOT_AVAILABLE; + } + + // Track the responding info for an OOP receiver page. + AddRespondingSessionId(aWindowId, aSessionId, + nsIPresentationService::ROLE_RECEIVER); + + Unused << NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady( + nsString(aSessionId), aWindowId, aIsLoading)); + + // Release mCallback after using aSessionId + // because aSessionId is held by mCallback. + mCallback = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId, + uint8_t aRole) { + PRES_DEBUG("content %s:id[%s], role[%d]\n", __func__, + NS_ConvertUTF16toUTF8(aSessionId).get(), aRole); + + if (nsIPresentationService::ROLE_RECEIVER == aRole) { + // Terminate receiver page. + uint64_t windowId; + if (NS_SUCCEEDED( + GetWindowIdBySessionIdInternal(aSessionId, aRole, &windowId))) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "dom::PresentationIPCService::UntrackSessionInfo", + [windowId]() -> void { + PRES_DEBUG("Attempt to close window[%" PRIu64 "]\n", windowId); + + if (auto* window = + nsGlobalWindowInner::GetInnerWindowWithId(windowId)) { + window->Close(); + } + })); + } + } + + // Remove the OOP responding info (if it has never been used). + RemoveRespondingSessionId(aSessionId, aRole); + + if (nsIPresentationService::ROLE_CONTROLLER == aRole) { + mSessionInfoAtController.Remove(aSessionId); + } else { + mSessionInfoAtReceiver.Remove(aSessionId); + } + + return NS_OK; +} + +void PresentationIPCService::NotifyPresentationChildDestroyed() { + sPresentationChild = nullptr; +} + +nsresult PresentationIPCService::MonitorResponderLoading( + const nsAString& aSessionId, nsIDocShell* aDocShell) { + MOZ_ASSERT(NS_IsMainThread()); + + mCallback = new PresentationResponderLoadingCallback(aSessionId); + return mCallback->Init(aDocShell); +} + +nsresult PresentationIPCService::CloseContentSessionTransport( + const nsString& aSessionId, uint8_t aRole, nsresult aReason) { + RefPtr<PresentationContentSessionInfo> info = + GetSessionInfo(aSessionId, aRole); + if (NS_WARN_IF(!info)) { + return NS_ERROR_NOT_AVAILABLE; + } + + return info->Close(aReason); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationIPCService.h b/dom/presentation/ipc/PresentationIPCService.h new file mode 100644 index 0000000000..9b80209a52 --- /dev/null +++ b/dom/presentation/ipc/PresentationIPCService.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PresentationIPCService_h +#define mozilla_dom_PresentationIPCService_h + +#include "mozilla/dom/PresentationServiceBase.h" +#include "nsIPresentationListener.h" +#include "nsIPresentationSessionTransport.h" +#include "nsIPresentationService.h" + +class nsIDocShell; + +namespace mozilla { +namespace dom { + +class PresentationIPCRequest; +class PresentationContentSessionInfo; +class PresentationResponderLoadingCallback; + +class PresentationIPCService final + : public nsIPresentationAvailabilityListener, + public nsIPresentationService, + public PresentationServiceBase<PresentationContentSessionInfo> { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER + NS_DECL_NSIPRESENTATIONSERVICE + + PresentationIPCService(); + + nsresult NotifySessionStateChange(const nsAString& aSessionId, + uint16_t aState, nsresult aReason); + + nsresult NotifyMessage(const nsAString& aSessionId, const nsACString& aData, + const bool& aIsBinary); + + nsresult NotifySessionConnect(uint64_t aWindowId, + const nsAString& aSessionId); + + void NotifyPresentationChildDestroyed(); + + nsresult MonitorResponderLoading(const nsAString& aSessionId, + nsIDocShell* aDocShell); + + nsresult NotifySessionTransport(const nsString& aSessionId, + const uint8_t& aRole, + nsIPresentationSessionTransport* transport); + + nsresult CloseContentSessionTransport(const nsString& aSessionId, + uint8_t aRole, nsresult aReason); + + private: + virtual ~PresentationIPCService(); + void SendRequest(nsIPresentationServiceCallback* aCallback, + const PresentationIPCRequest& aRequest); + + nsRefPtrHashtable<nsStringHashKey, nsIPresentationSessionListener> + mSessionListeners; + nsRefPtrHashtable<nsUint64HashKey, nsIPresentationRespondingListener> + mRespondingListeners; + RefPtr<PresentationResponderLoadingCallback> mCallback; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PresentationIPCService_h diff --git a/dom/presentation/ipc/PresentationParent.cpp b/dom/presentation/ipc/PresentationParent.cpp new file mode 100644 index 0000000000..8d643bd8b3 --- /dev/null +++ b/dom/presentation/ipc/PresentationParent.cpp @@ -0,0 +1,497 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DCPresentationChannelDescription.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/ContentProcessManager.h" +#include "mozilla/dom/Element.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/Unused.h" +#include "nsComponentManagerUtils.h" +#include "nsIPresentationSessionTransport.h" +#include "nsIPresentationSessionTransportBuilder.h" +#include "nsServiceManagerUtils.h" +#include "PresentationBuilderParent.h" +#include "PresentationParent.h" +#include "PresentationService.h" +#include "PresentationSessionInfo.h" + +namespace mozilla { +namespace dom { + +namespace { + +class PresentationTransportBuilderConstructorIPC final + : public nsIPresentationTransportBuilderConstructor { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR + + explicit PresentationTransportBuilderConstructorIPC( + PresentationParent* aParent) + : mParent(aParent) {} + + private: + virtual ~PresentationTransportBuilderConstructorIPC() = default; + + RefPtr<PresentationParent> mParent; +}; + +NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC, + nsIPresentationTransportBuilderConstructor) + +NS_IMETHODIMP +PresentationTransportBuilderConstructorIPC::CreateTransportBuilder( + uint8_t aType, nsIPresentationSessionTransportBuilder** aRetval) { + if (NS_WARN_IF(!aRetval)) { + return NS_ERROR_INVALID_ARG; + } + + *aRetval = nullptr; + + if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP && + aType != + nsIPresentationChannelDescription::TYPE_DATACHANNEL)) { + return NS_ERROR_INVALID_ARG; + } + + if (XRE_IsContentProcess()) { + MOZ_ASSERT(false, + "CreateTransportBuilder can only be invoked in parent process."); + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIPresentationSessionTransportBuilder> builder; + if (aType == nsIPresentationChannelDescription::TYPE_TCP) { + builder = do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID); + } else { + builder = new PresentationBuilderParent(mParent); + } + + if (NS_WARN_IF(!builder)) { + return NS_ERROR_DOM_OPERATION_ERR; + } + + builder.forget(aRetval); + return NS_OK; +} + +} // anonymous namespace + +/* + * Implementation of PresentationParent + */ + +NS_IMPL_ISUPPORTS(PresentationParent, nsIPresentationAvailabilityListener, + nsIPresentationSessionListener, + nsIPresentationRespondingListener) + +PresentationParent::PresentationParent() = default; + +/* virtual */ PresentationParent::~PresentationParent() = default; + +bool PresentationParent::Init(ContentParentId aContentParentId) { + MOZ_ASSERT(!mService); + mService = do_GetService(PRESENTATION_SERVICE_CONTRACTID); + mChildId = aContentParentId; + return !NS_WARN_IF(!mService); +} + +void PresentationParent::ActorDestroy(ActorDestroyReason aWhy) { + mActorDestroyed = true; + + for (uint32_t i = 0; i < mSessionIdsAtController.Length(); i++) { + Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener( + mSessionIdsAtController[i], nsIPresentationService::ROLE_CONTROLLER))); + } + mSessionIdsAtController.Clear(); + + for (uint32_t i = 0; i < mSessionIdsAtReceiver.Length(); i++) { + Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener( + mSessionIdsAtReceiver[i], nsIPresentationService::ROLE_RECEIVER))); + } + mSessionIdsAtReceiver.Clear(); + + for (uint32_t i = 0; i < mWindowIds.Length(); i++) { + Unused << NS_WARN_IF( + NS_FAILED(mService->UnregisterRespondingListener(mWindowIds[i]))); + } + mWindowIds.Clear(); + + if (!mContentAvailabilityUrls.IsEmpty()) { + mService->UnregisterAvailabilityListener(mContentAvailabilityUrls, this); + } + mService = nullptr; +} + +mozilla::ipc::IPCResult PresentationParent::RecvPPresentationRequestConstructor( + PPresentationRequestParent* aActor, + const PresentationIPCRequest& aRequest) { + PresentationRequestParent* actor = + static_cast<PresentationRequestParent*>(aActor); + + nsresult rv = NS_ERROR_FAILURE; + switch (aRequest.type()) { + case PresentationIPCRequest::TStartSessionRequest: + rv = actor->DoRequest(aRequest.get_StartSessionRequest()); + break; + case PresentationIPCRequest::TSendSessionMessageRequest: + rv = actor->DoRequest(aRequest.get_SendSessionMessageRequest()); + break; + case PresentationIPCRequest::TCloseSessionRequest: + rv = actor->DoRequest(aRequest.get_CloseSessionRequest()); + break; + case PresentationIPCRequest::TTerminateSessionRequest: + rv = actor->DoRequest(aRequest.get_TerminateSessionRequest()); + break; + case PresentationIPCRequest::TReconnectSessionRequest: + rv = actor->DoRequest(aRequest.get_ReconnectSessionRequest()); + break; + case PresentationIPCRequest::TBuildTransportRequest: + rv = actor->DoRequest(aRequest.get_BuildTransportRequest()); + break; + default: + MOZ_CRASH("Unknown PresentationIPCRequest type"); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +PPresentationRequestParent* PresentationParent::AllocPPresentationRequestParent( + const PresentationIPCRequest& aRequest) { + MOZ_ASSERT(mService); + RefPtr<PresentationRequestParent> actor = + new PresentationRequestParent(mService, mChildId); + return actor.forget().take(); +} + +bool PresentationParent::DeallocPPresentationRequestParent( + PPresentationRequestParent* aActor) { + RefPtr<PresentationRequestParent> actor = + dont_AddRef(static_cast<PresentationRequestParent*>(aActor)); + return true; +} + +PPresentationBuilderParent* PresentationParent::AllocPPresentationBuilderParent( + const nsString& aSessionId, const uint8_t& aRole) { + MOZ_ASSERT_UNREACHABLE( + "We should never be manually allocating " + "AllocPPresentationBuilderParent actors"); + return nullptr; +} + +bool PresentationParent::DeallocPPresentationBuilderParent( + PPresentationBuilderParent* aActor) { + return true; +} + +mozilla::ipc::IPCResult PresentationParent::Recv__delete__() { + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationParent::RecvRegisterAvailabilityHandler( + nsTArray<nsString>&& aAvailabilityUrls) { + MOZ_ASSERT(mService); + + Unused << NS_WARN_IF(NS_FAILED( + mService->RegisterAvailabilityListener(aAvailabilityUrls, this))); + mContentAvailabilityUrls.AppendElements(aAvailabilityUrls); + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationParent::RecvUnregisterAvailabilityHandler( + nsTArray<nsString>&& aAvailabilityUrls) { + MOZ_ASSERT(mService); + + Unused << NS_WARN_IF(NS_FAILED( + mService->UnregisterAvailabilityListener(aAvailabilityUrls, this))); + for (const auto& url : aAvailabilityUrls) { + mContentAvailabilityUrls.RemoveElement(url); + } + return IPC_OK(); +} + +/* virtual */ mozilla::ipc::IPCResult +PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId, + const uint8_t& aRole) { + MOZ_ASSERT(mService); + + // Validate the accessibility (primarily for receiver side) so that a + // compromised child process can't fake the ID. + if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get()) + ->IsSessionAccessible(aSessionId, aRole, OtherPid()))) { + return IPC_OK(); + } + + if (nsIPresentationService::ROLE_CONTROLLER == aRole) { + mSessionIdsAtController.AppendElement(aSessionId); + } else { + mSessionIdsAtReceiver.AppendElement(aSessionId); + } + Unused << NS_WARN_IF( + NS_FAILED(mService->RegisterSessionListener(aSessionId, aRole, this))); + return IPC_OK(); +} + +/* virtual */ mozilla::ipc::IPCResult +PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId, + const uint8_t& aRole) { + MOZ_ASSERT(mService); + if (nsIPresentationService::ROLE_CONTROLLER == aRole) { + mSessionIdsAtController.RemoveElement(aSessionId); + } else { + mSessionIdsAtReceiver.RemoveElement(aSessionId); + } + Unused << NS_WARN_IF( + NS_FAILED(mService->UnregisterSessionListener(aSessionId, aRole))); + return IPC_OK(); +} + +/* virtual */ mozilla::ipc::IPCResult +PresentationParent::RecvRegisterRespondingHandler(const uint64_t& aWindowId) { + MOZ_ASSERT(mService); + + mWindowIds.AppendElement(aWindowId); + Unused << NS_WARN_IF( + NS_FAILED(mService->RegisterRespondingListener(aWindowId, this))); + return IPC_OK(); +} + +/* virtual */ mozilla::ipc::IPCResult +PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId) { + MOZ_ASSERT(mService); + mWindowIds.RemoveElement(aWindowId); + Unused << NS_WARN_IF( + NS_FAILED(mService->UnregisterRespondingListener(aWindowId))); + return IPC_OK(); +} + +NS_IMETHODIMP +PresentationParent::NotifyAvailableChange( + const nsTArray<nsString>& aAvailabilityUrls, bool aAvailable) { + if (NS_WARN_IF(mActorDestroyed || + !SendNotifyAvailableChange(aAvailabilityUrls, aAvailable))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationParent::NotifyStateChange(const nsAString& aSessionId, + uint16_t aState, nsresult aReason) { + if (NS_WARN_IF(mActorDestroyed || + !SendNotifySessionStateChange(nsString(aSessionId), aState, + aReason))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationParent::NotifyMessage(const nsAString& aSessionId, + const nsACString& aData, bool aIsBinary) { + if (NS_WARN_IF(mActorDestroyed || + !SendNotifyMessage(nsString(aSessionId), nsCString(aData), + aIsBinary))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PresentationParent::NotifySessionConnect(uint64_t aWindowId, + const nsAString& aSessionId) { + if (NS_WARN_IF(mActorDestroyed || + !SendNotifySessionConnect(aWindowId, nsString(aSessionId)))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +mozilla::ipc::IPCResult PresentationParent::RecvNotifyReceiverReady( + const nsString& aSessionId, const uint64_t& aWindowId, + const bool& aIsLoading) { + MOZ_ASSERT(mService); + + nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor = + new PresentationTransportBuilderConstructorIPC(this); + Unused << NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady( + aSessionId, aWindowId, aIsLoading, constructor))); + return IPC_OK(); +} + +mozilla::ipc::IPCResult PresentationParent::RecvNotifyTransportClosed( + const nsString& aSessionId, const uint8_t& aRole, const nsresult& aReason) { + MOZ_ASSERT(mService); + + Unused << NS_WARN_IF( + NS_FAILED(mService->NotifyTransportClosed(aSessionId, aRole, aReason))); + return IPC_OK(); +} + +/* + * Implementation of PresentationRequestParent + */ + +NS_IMPL_ISUPPORTS(PresentationRequestParent, nsIPresentationServiceCallback) + +PresentationRequestParent::PresentationRequestParent( + nsIPresentationService* aService, ContentParentId aContentParentId) + : mService(aService), mChildId(aContentParentId) {} + +PresentationRequestParent::~PresentationRequestParent() = default; + +void PresentationRequestParent::ActorDestroy(ActorDestroyReason aWhy) { + mActorDestroyed = true; + mService = nullptr; +} + +nsresult PresentationRequestParent::DoRequest( + const StartSessionRequest& aRequest) { + MOZ_ASSERT(mService); + + mSessionId = aRequest.sessionId(); + + RefPtr<EventTarget> eventTarget; + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + RefPtr<BrowserParent> tp = cpm->GetTopLevelBrowserParentByProcessAndTabId( + mChildId, aRequest.tabId()); + if (tp) { + eventTarget = tp->GetOwnerElement(); + } + + RefPtr<PresentationParent> parent = + static_cast<PresentationParent*>(Manager()); + nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor = + new PresentationTransportBuilderConstructorIPC(parent); + return mService->StartSession(aRequest.urls(), aRequest.sessionId(), + aRequest.origin(), aRequest.deviceId(), + aRequest.windowId(), eventTarget, + aRequest.principal(), this, constructor); +} + +nsresult PresentationRequestParent::DoRequest( + const SendSessionMessageRequest& aRequest) { + MOZ_ASSERT(mService); + + // Validate the accessibility (primarily for receiver side) so that a + // compromised child process can't fake the ID. + if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get()) + ->IsSessionAccessible(aRequest.sessionId(), + aRequest.role(), OtherPid()))) { + return SendResponse(NS_ERROR_DOM_SECURITY_ERR); + } + + nsresult rv = mService->SendSessionMessage(aRequest.sessionId(), + aRequest.role(), aRequest.data()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return SendResponse(rv); + } + return SendResponse(NS_OK); +} + +nsresult PresentationRequestParent::DoRequest( + const CloseSessionRequest& aRequest) { + MOZ_ASSERT(mService); + + // Validate the accessibility (primarily for receiver side) so that a + // compromised child process can't fake the ID. + if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get()) + ->IsSessionAccessible(aRequest.sessionId(), + aRequest.role(), OtherPid()))) { + return SendResponse(NS_ERROR_DOM_SECURITY_ERR); + } + + nsresult rv = mService->CloseSession(aRequest.sessionId(), aRequest.role(), + aRequest.closedReason()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return SendResponse(rv); + } + return SendResponse(NS_OK); +} + +nsresult PresentationRequestParent::DoRequest( + const TerminateSessionRequest& aRequest) { + MOZ_ASSERT(mService); + + // Validate the accessibility (primarily for receiver side) so that a + // compromised child process can't fake the ID. + if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get()) + ->IsSessionAccessible(aRequest.sessionId(), + aRequest.role(), OtherPid()))) { + return SendResponse(NS_ERROR_DOM_SECURITY_ERR); + } + + nsresult rv = + mService->TerminateSession(aRequest.sessionId(), aRequest.role()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return SendResponse(rv); + } + return SendResponse(NS_OK); +} + +nsresult PresentationRequestParent::DoRequest( + const ReconnectSessionRequest& aRequest) { + MOZ_ASSERT(mService); + + // Validate the accessibility (primarily for receiver side) so that a + // compromised child process can't fake the ID. + if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get()) + ->IsSessionAccessible(aRequest.sessionId(), + aRequest.role(), OtherPid()))) { + // NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec. + // https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation + return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR); + } + + mSessionId = aRequest.sessionId(); + return mService->ReconnectSession(aRequest.urls(), aRequest.sessionId(), + aRequest.role(), this); +} + +nsresult PresentationRequestParent::DoRequest( + const BuildTransportRequest& aRequest) { + MOZ_ASSERT(mService); + + // Validate the accessibility (primarily for receiver side) so that a + // compromised child process can't fake the ID. + if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get()) + ->IsSessionAccessible(aRequest.sessionId(), + aRequest.role(), OtherPid()))) { + return SendResponse(NS_ERROR_DOM_SECURITY_ERR); + } + + nsresult rv = mService->BuildTransport(aRequest.sessionId(), aRequest.role()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return SendResponse(rv); + } + return SendResponse(NS_OK); +} + +NS_IMETHODIMP +PresentationRequestParent::NotifySuccess(const nsAString& aUrl) { + Unused << SendNotifyRequestUrlSelected(nsString(aUrl)); + return SendResponse(NS_OK); +} + +NS_IMETHODIMP +PresentationRequestParent::NotifyError(nsresult aError) { + return SendResponse(aError); +} + +nsresult PresentationRequestParent::SendResponse(nsresult aResult) { + if (NS_WARN_IF(mActorDestroyed || !Send__delete__(this, aResult))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationParent.h b/dom/presentation/ipc/PresentationParent.h new file mode 100644 index 0000000000..c50d374923 --- /dev/null +++ b/dom/presentation/ipc/PresentationParent.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PresentationParent_h__ +#define mozilla_dom_PresentationParent_h__ + +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/dom/PPresentationBuilderParent.h" +#include "mozilla/dom/PPresentationParent.h" +#include "mozilla/dom/PPresentationRequestParent.h" +#include "nsIPresentationListener.h" +#include "nsIPresentationService.h" + +namespace mozilla { +namespace dom { + +class PresentationParent final : public PPresentationParent, + public nsIPresentationAvailabilityListener, + public nsIPresentationSessionListener, + public nsIPresentationRespondingListener { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER + NS_DECL_NSIPRESENTATIONSESSIONLISTENER + NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER + + PresentationParent(); + + bool Init(ContentParentId aContentParentId); + + bool RegisterTransportBuilder(const nsString& aSessionId, + const uint8_t& aRole); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual mozilla::ipc::IPCResult RecvPPresentationRequestConstructor( + PPresentationRequestParent* aActor, + const PresentationIPCRequest& aRequest) override; + + PPresentationRequestParent* AllocPPresentationRequestParent( + const PresentationIPCRequest& aRequest); + + bool DeallocPPresentationRequestParent(PPresentationRequestParent* aActor); + + PPresentationBuilderParent* AllocPPresentationBuilderParent( + const nsString& aSessionId, const uint8_t& aRole); + + bool DeallocPPresentationBuilderParent(PPresentationBuilderParent* aActor); + + virtual mozilla::ipc::IPCResult Recv__delete__() override; + + mozilla::ipc::IPCResult RecvRegisterAvailabilityHandler( + nsTArray<nsString>&& aAvailabilityUrls); + + mozilla::ipc::IPCResult RecvUnregisterAvailabilityHandler( + nsTArray<nsString>&& aAvailabilityUrls); + + mozilla::ipc::IPCResult RecvRegisterSessionHandler(const nsString& aSessionId, + const uint8_t& aRole); + + mozilla::ipc::IPCResult RecvUnregisterSessionHandler( + const nsString& aSessionId, const uint8_t& aRole); + + mozilla::ipc::IPCResult RecvRegisterRespondingHandler( + const uint64_t& aWindowId); + + mozilla::ipc::IPCResult RecvUnregisterRespondingHandler( + const uint64_t& aWindowId); + + mozilla::ipc::IPCResult RecvNotifyReceiverReady(const nsString& aSessionId, + const uint64_t& aWindowId, + const bool& aIsLoading); + + mozilla::ipc::IPCResult RecvNotifyTransportClosed(const nsString& aSessionId, + const uint8_t& aRole, + const nsresult& aReason); + + private: + virtual ~PresentationParent(); + + bool mActorDestroyed = false; + nsCOMPtr<nsIPresentationService> mService; + nsTArray<nsString> mSessionIdsAtController; + nsTArray<nsString> mSessionIdsAtReceiver; + nsTArray<uint64_t> mWindowIds; + ContentParentId mChildId; + nsTArray<nsString> mContentAvailabilityUrls; +}; + +class PresentationRequestParent final : public PPresentationRequestParent, + public nsIPresentationServiceCallback { + friend class PresentationParent; + + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPRESENTATIONSERVICECALLBACK + + explicit PresentationRequestParent(nsIPresentationService* aService, + ContentParentId aContentParentId); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + virtual ~PresentationRequestParent(); + + nsresult SendResponse(nsresult aResult); + + nsresult DoRequest(const StartSessionRequest& aRequest); + + nsresult DoRequest(const SendSessionMessageRequest& aRequest); + + nsresult DoRequest(const CloseSessionRequest& aRequest); + + nsresult DoRequest(const TerminateSessionRequest& aRequest); + + nsresult DoRequest(const ReconnectSessionRequest& aRequest); + + nsresult DoRequest(const BuildTransportRequest& aRequest); + + bool mActorDestroyed = false; + bool mNeedRegisterBuilder = false; + nsString mSessionId; + nsCOMPtr<nsIPresentationService> mService; + ContentParentId mChildId; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PresentationParent_h__ |