1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "DCPresentationChannelDescription.h"
8 #include "mozilla/dom/ContentProcessManager.h"
9 #include "mozilla/dom/Element.h"
10 #include "mozilla/ipc/InputStreamUtils.h"
11 #include "mozilla/Unused.h"
12 #include "nsIPresentationSessionTransport.h"
13 #include "nsIPresentationSessionTransportBuilder.h"
14 #include "nsServiceManagerUtils.h"
15 #include "PresentationBuilderParent.h"
16 #include "PresentationParent.h"
17 #include "PresentationService.h"
18 #include "PresentationSessionInfo.h"
19 
20 namespace mozilla {
21 namespace dom {
22 
23 namespace {
24 
25 class PresentationTransportBuilderConstructorIPC final
26     : public nsIPresentationTransportBuilderConstructor {
27  public:
28   NS_DECL_ISUPPORTS
29   NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
30 
PresentationTransportBuilderConstructorIPC(PresentationParent * aParent)31   explicit PresentationTransportBuilderConstructorIPC(
32       PresentationParent* aParent)
33       : mParent(aParent) {}
34 
35  private:
36   virtual ~PresentationTransportBuilderConstructorIPC() = default;
37 
38   RefPtr<PresentationParent> mParent;
39 };
40 
NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC,nsIPresentationTransportBuilderConstructor)41 NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC,
42                   nsIPresentationTransportBuilderConstructor)
43 
44 NS_IMETHODIMP
45 PresentationTransportBuilderConstructorIPC::CreateTransportBuilder(
46     uint8_t aType, nsIPresentationSessionTransportBuilder** aRetval) {
47   if (NS_WARN_IF(!aRetval)) {
48     return NS_ERROR_INVALID_ARG;
49   }
50 
51   *aRetval = nullptr;
52 
53   if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
54                  aType !=
55                      nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
56     return NS_ERROR_INVALID_ARG;
57   }
58 
59   if (XRE_IsContentProcess()) {
60     MOZ_ASSERT(false,
61                "CreateTransportBuilder can only be invoked in parent process.");
62     return NS_ERROR_FAILURE;
63   }
64 
65   nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
66   if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
67     builder = do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
68   } else {
69     builder = new PresentationBuilderParent(mParent);
70   }
71 
72   if (NS_WARN_IF(!builder)) {
73     return NS_ERROR_DOM_OPERATION_ERR;
74   }
75 
76   builder.forget(aRetval);
77   return NS_OK;
78 }
79 
80 }  // anonymous namespace
81 
82 /*
83  * Implementation of PresentationParent
84  */
85 
86 NS_IMPL_ISUPPORTS(PresentationParent, nsIPresentationAvailabilityListener,
87                   nsIPresentationSessionListener,
88                   nsIPresentationRespondingListener)
89 
90 PresentationParent::PresentationParent() = default;
91 
92 /* virtual */ PresentationParent::~PresentationParent() = default;
93 
Init(ContentParentId aContentParentId)94 bool PresentationParent::Init(ContentParentId aContentParentId) {
95   MOZ_ASSERT(!mService);
96   mService = do_GetService(PRESENTATION_SERVICE_CONTRACTID);
97   mChildId = aContentParentId;
98   return !NS_WARN_IF(!mService);
99 }
100 
ActorDestroy(ActorDestroyReason aWhy)101 void PresentationParent::ActorDestroy(ActorDestroyReason aWhy) {
102   mActorDestroyed = true;
103 
104   for (uint32_t i = 0; i < mSessionIdsAtController.Length(); i++) {
105     Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(
106         mSessionIdsAtController[i], nsIPresentationService::ROLE_CONTROLLER)));
107   }
108   mSessionIdsAtController.Clear();
109 
110   for (uint32_t i = 0; i < mSessionIdsAtReceiver.Length(); i++) {
111     Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(
112         mSessionIdsAtReceiver[i], nsIPresentationService::ROLE_RECEIVER)));
113   }
114   mSessionIdsAtReceiver.Clear();
115 
116   for (uint32_t i = 0; i < mWindowIds.Length(); i++) {
117     Unused << NS_WARN_IF(
118         NS_FAILED(mService->UnregisterRespondingListener(mWindowIds[i])));
119   }
120   mWindowIds.Clear();
121 
122   if (!mContentAvailabilityUrls.IsEmpty()) {
123     mService->UnregisterAvailabilityListener(mContentAvailabilityUrls, this);
124   }
125   mService = nullptr;
126 }
127 
RecvPPresentationRequestConstructor(PPresentationRequestParent * aActor,const PresentationIPCRequest & aRequest)128 mozilla::ipc::IPCResult PresentationParent::RecvPPresentationRequestConstructor(
129     PPresentationRequestParent* aActor,
130     const PresentationIPCRequest& aRequest) {
131   PresentationRequestParent* actor =
132       static_cast<PresentationRequestParent*>(aActor);
133 
134   nsresult rv = NS_ERROR_FAILURE;
135   switch (aRequest.type()) {
136     case PresentationIPCRequest::TStartSessionRequest:
137       rv = actor->DoRequest(aRequest.get_StartSessionRequest());
138       break;
139     case PresentationIPCRequest::TSendSessionMessageRequest:
140       rv = actor->DoRequest(aRequest.get_SendSessionMessageRequest());
141       break;
142     case PresentationIPCRequest::TCloseSessionRequest:
143       rv = actor->DoRequest(aRequest.get_CloseSessionRequest());
144       break;
145     case PresentationIPCRequest::TTerminateSessionRequest:
146       rv = actor->DoRequest(aRequest.get_TerminateSessionRequest());
147       break;
148     case PresentationIPCRequest::TReconnectSessionRequest:
149       rv = actor->DoRequest(aRequest.get_ReconnectSessionRequest());
150       break;
151     case PresentationIPCRequest::TBuildTransportRequest:
152       rv = actor->DoRequest(aRequest.get_BuildTransportRequest());
153       break;
154     default:
155       MOZ_CRASH("Unknown PresentationIPCRequest type");
156   }
157 
158   if (NS_WARN_IF(NS_FAILED(rv))) {
159     return IPC_FAIL_NO_REASON(this);
160   }
161   return IPC_OK();
162 }
163 
AllocPPresentationRequestParent(const PresentationIPCRequest & aRequest)164 PPresentationRequestParent* PresentationParent::AllocPPresentationRequestParent(
165     const PresentationIPCRequest& aRequest) {
166   MOZ_ASSERT(mService);
167   RefPtr<PresentationRequestParent> actor =
168       new PresentationRequestParent(mService, mChildId);
169   return actor.forget().take();
170 }
171 
DeallocPPresentationRequestParent(PPresentationRequestParent * aActor)172 bool PresentationParent::DeallocPPresentationRequestParent(
173     PPresentationRequestParent* aActor) {
174   RefPtr<PresentationRequestParent> actor =
175       dont_AddRef(static_cast<PresentationRequestParent*>(aActor));
176   return true;
177 }
178 
AllocPPresentationBuilderParent(const nsString & aSessionId,const uint8_t & aRole)179 PPresentationBuilderParent* PresentationParent::AllocPPresentationBuilderParent(
180     const nsString& aSessionId, const uint8_t& aRole) {
181   MOZ_ASSERT_UNREACHABLE(
182       "We should never be manually allocating "
183       "AllocPPresentationBuilderParent actors");
184   return nullptr;
185 }
186 
DeallocPPresentationBuilderParent(PPresentationBuilderParent * aActor)187 bool PresentationParent::DeallocPPresentationBuilderParent(
188     PPresentationBuilderParent* aActor) {
189   return true;
190 }
191 
Recv__delete__()192 mozilla::ipc::IPCResult PresentationParent::Recv__delete__() {
193   return IPC_OK();
194 }
195 
RecvRegisterAvailabilityHandler(nsTArray<nsString> && aAvailabilityUrls)196 mozilla::ipc::IPCResult PresentationParent::RecvRegisterAvailabilityHandler(
197     nsTArray<nsString>&& aAvailabilityUrls) {
198   MOZ_ASSERT(mService);
199 
200   Unused << NS_WARN_IF(NS_FAILED(
201       mService->RegisterAvailabilityListener(aAvailabilityUrls, this)));
202   mContentAvailabilityUrls.AppendElements(aAvailabilityUrls);
203   return IPC_OK();
204 }
205 
RecvUnregisterAvailabilityHandler(nsTArray<nsString> && aAvailabilityUrls)206 mozilla::ipc::IPCResult PresentationParent::RecvUnregisterAvailabilityHandler(
207     nsTArray<nsString>&& aAvailabilityUrls) {
208   MOZ_ASSERT(mService);
209 
210   Unused << NS_WARN_IF(NS_FAILED(
211       mService->UnregisterAvailabilityListener(aAvailabilityUrls, this)));
212   for (const auto& url : aAvailabilityUrls) {
213     mContentAvailabilityUrls.RemoveElement(url);
214   }
215   return IPC_OK();
216 }
217 
218 /* virtual */ mozilla::ipc::IPCResult
RecvRegisterSessionHandler(const nsString & aSessionId,const uint8_t & aRole)219 PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId,
220                                                const uint8_t& aRole) {
221   MOZ_ASSERT(mService);
222 
223   // Validate the accessibility (primarily for receiver side) so that a
224   // compromised child process can't fake the ID.
225   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
226                       ->IsSessionAccessible(aSessionId, aRole, OtherPid()))) {
227     return IPC_OK();
228   }
229 
230   if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
231     mSessionIdsAtController.AppendElement(aSessionId);
232   } else {
233     mSessionIdsAtReceiver.AppendElement(aSessionId);
234   }
235   Unused << NS_WARN_IF(
236       NS_FAILED(mService->RegisterSessionListener(aSessionId, aRole, this)));
237   return IPC_OK();
238 }
239 
240 /* virtual */ mozilla::ipc::IPCResult
RecvUnregisterSessionHandler(const nsString & aSessionId,const uint8_t & aRole)241 PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId,
242                                                  const uint8_t& aRole) {
243   MOZ_ASSERT(mService);
244   if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
245     mSessionIdsAtController.RemoveElement(aSessionId);
246   } else {
247     mSessionIdsAtReceiver.RemoveElement(aSessionId);
248   }
249   Unused << NS_WARN_IF(
250       NS_FAILED(mService->UnregisterSessionListener(aSessionId, aRole)));
251   return IPC_OK();
252 }
253 
254 /* virtual */ mozilla::ipc::IPCResult
RecvRegisterRespondingHandler(const uint64_t & aWindowId)255 PresentationParent::RecvRegisterRespondingHandler(const uint64_t& aWindowId) {
256   MOZ_ASSERT(mService);
257 
258   mWindowIds.AppendElement(aWindowId);
259   Unused << NS_WARN_IF(
260       NS_FAILED(mService->RegisterRespondingListener(aWindowId, this)));
261   return IPC_OK();
262 }
263 
264 /* virtual */ mozilla::ipc::IPCResult
RecvUnregisterRespondingHandler(const uint64_t & aWindowId)265 PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId) {
266   MOZ_ASSERT(mService);
267   mWindowIds.RemoveElement(aWindowId);
268   Unused << NS_WARN_IF(
269       NS_FAILED(mService->UnregisterRespondingListener(aWindowId)));
270   return IPC_OK();
271 }
272 
273 NS_IMETHODIMP
NotifyAvailableChange(const nsTArray<nsString> & aAvailabilityUrls,bool aAvailable)274 PresentationParent::NotifyAvailableChange(
275     const nsTArray<nsString>& aAvailabilityUrls, bool aAvailable) {
276   if (NS_WARN_IF(mActorDestroyed ||
277                  !SendNotifyAvailableChange(aAvailabilityUrls, aAvailable))) {
278     return NS_ERROR_FAILURE;
279   }
280   return NS_OK;
281 }
282 
283 NS_IMETHODIMP
NotifyStateChange(const nsAString & aSessionId,uint16_t aState,nsresult aReason)284 PresentationParent::NotifyStateChange(const nsAString& aSessionId,
285                                       uint16_t aState, nsresult aReason) {
286   if (NS_WARN_IF(mActorDestroyed ||
287                  !SendNotifySessionStateChange(nsString(aSessionId), aState,
288                                                aReason))) {
289     return NS_ERROR_FAILURE;
290   }
291   return NS_OK;
292 }
293 
294 NS_IMETHODIMP
NotifyMessage(const nsAString & aSessionId,const nsACString & aData,bool aIsBinary)295 PresentationParent::NotifyMessage(const nsAString& aSessionId,
296                                   const nsACString& aData, bool aIsBinary) {
297   if (NS_WARN_IF(mActorDestroyed ||
298                  !SendNotifyMessage(nsString(aSessionId), nsCString(aData),
299                                     aIsBinary))) {
300     return NS_ERROR_FAILURE;
301   }
302   return NS_OK;
303 }
304 
305 NS_IMETHODIMP
NotifySessionConnect(uint64_t aWindowId,const nsAString & aSessionId)306 PresentationParent::NotifySessionConnect(uint64_t aWindowId,
307                                          const nsAString& aSessionId) {
308   if (NS_WARN_IF(mActorDestroyed ||
309                  !SendNotifySessionConnect(aWindowId, nsString(aSessionId)))) {
310     return NS_ERROR_FAILURE;
311   }
312   return NS_OK;
313 }
314 
RecvNotifyReceiverReady(const nsString & aSessionId,const uint64_t & aWindowId,const bool & aIsLoading)315 mozilla::ipc::IPCResult PresentationParent::RecvNotifyReceiverReady(
316     const nsString& aSessionId, const uint64_t& aWindowId,
317     const bool& aIsLoading) {
318   MOZ_ASSERT(mService);
319 
320   nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
321       new PresentationTransportBuilderConstructorIPC(this);
322   Unused << NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(
323       aSessionId, aWindowId, aIsLoading, constructor)));
324   return IPC_OK();
325 }
326 
RecvNotifyTransportClosed(const nsString & aSessionId,const uint8_t & aRole,const nsresult & aReason)327 mozilla::ipc::IPCResult PresentationParent::RecvNotifyTransportClosed(
328     const nsString& aSessionId, const uint8_t& aRole, const nsresult& aReason) {
329   MOZ_ASSERT(mService);
330 
331   Unused << NS_WARN_IF(
332       NS_FAILED(mService->NotifyTransportClosed(aSessionId, aRole, aReason)));
333   return IPC_OK();
334 }
335 
336 /*
337  * Implementation of PresentationRequestParent
338  */
339 
NS_IMPL_ISUPPORTS(PresentationRequestParent,nsIPresentationServiceCallback)340 NS_IMPL_ISUPPORTS(PresentationRequestParent, nsIPresentationServiceCallback)
341 
342 PresentationRequestParent::PresentationRequestParent(
343     nsIPresentationService* aService, ContentParentId aContentParentId)
344     : mService(aService), mChildId(aContentParentId) {}
345 
346 PresentationRequestParent::~PresentationRequestParent() = default;
347 
ActorDestroy(ActorDestroyReason aWhy)348 void PresentationRequestParent::ActorDestroy(ActorDestroyReason aWhy) {
349   mActorDestroyed = true;
350   mService = nullptr;
351 }
352 
DoRequest(const StartSessionRequest & aRequest)353 nsresult PresentationRequestParent::DoRequest(
354     const StartSessionRequest& aRequest) {
355   MOZ_ASSERT(mService);
356 
357   mSessionId = aRequest.sessionId();
358 
359   RefPtr<EventTarget> eventTarget;
360   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
361   RefPtr<BrowserParent> tp = cpm->GetTopLevelBrowserParentByProcessAndTabId(
362       mChildId, aRequest.tabId());
363   if (tp) {
364     eventTarget = tp->GetOwnerElement();
365   }
366 
367   RefPtr<PresentationParent> parent =
368       static_cast<PresentationParent*>(Manager());
369   nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
370       new PresentationTransportBuilderConstructorIPC(parent);
371   return mService->StartSession(aRequest.urls(), aRequest.sessionId(),
372                                 aRequest.origin(), aRequest.deviceId(),
373                                 aRequest.windowId(), eventTarget,
374                                 aRequest.principal(), this, constructor);
375 }
376 
DoRequest(const SendSessionMessageRequest & aRequest)377 nsresult PresentationRequestParent::DoRequest(
378     const SendSessionMessageRequest& aRequest) {
379   MOZ_ASSERT(mService);
380 
381   // Validate the accessibility (primarily for receiver side) so that a
382   // compromised child process can't fake the ID.
383   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
384                       ->IsSessionAccessible(aRequest.sessionId(),
385                                             aRequest.role(), OtherPid()))) {
386     return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
387   }
388 
389   nsresult rv = mService->SendSessionMessage(aRequest.sessionId(),
390                                              aRequest.role(), aRequest.data());
391   if (NS_WARN_IF(NS_FAILED(rv))) {
392     return SendResponse(rv);
393   }
394   return SendResponse(NS_OK);
395 }
396 
DoRequest(const CloseSessionRequest & aRequest)397 nsresult PresentationRequestParent::DoRequest(
398     const CloseSessionRequest& aRequest) {
399   MOZ_ASSERT(mService);
400 
401   // Validate the accessibility (primarily for receiver side) so that a
402   // compromised child process can't fake the ID.
403   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
404                       ->IsSessionAccessible(aRequest.sessionId(),
405                                             aRequest.role(), OtherPid()))) {
406     return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
407   }
408 
409   nsresult rv = mService->CloseSession(aRequest.sessionId(), aRequest.role(),
410                                        aRequest.closedReason());
411   if (NS_WARN_IF(NS_FAILED(rv))) {
412     return SendResponse(rv);
413   }
414   return SendResponse(NS_OK);
415 }
416 
DoRequest(const TerminateSessionRequest & aRequest)417 nsresult PresentationRequestParent::DoRequest(
418     const TerminateSessionRequest& aRequest) {
419   MOZ_ASSERT(mService);
420 
421   // Validate the accessibility (primarily for receiver side) so that a
422   // compromised child process can't fake the ID.
423   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
424                       ->IsSessionAccessible(aRequest.sessionId(),
425                                             aRequest.role(), OtherPid()))) {
426     return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
427   }
428 
429   nsresult rv =
430       mService->TerminateSession(aRequest.sessionId(), aRequest.role());
431   if (NS_WARN_IF(NS_FAILED(rv))) {
432     return SendResponse(rv);
433   }
434   return SendResponse(NS_OK);
435 }
436 
DoRequest(const ReconnectSessionRequest & aRequest)437 nsresult PresentationRequestParent::DoRequest(
438     const ReconnectSessionRequest& aRequest) {
439   MOZ_ASSERT(mService);
440 
441   // Validate the accessibility (primarily for receiver side) so that a
442   // compromised child process can't fake the ID.
443   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
444                       ->IsSessionAccessible(aRequest.sessionId(),
445                                             aRequest.role(), OtherPid()))) {
446     // NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec.
447     // https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation
448     return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR);
449   }
450 
451   mSessionId = aRequest.sessionId();
452   return mService->ReconnectSession(aRequest.urls(), aRequest.sessionId(),
453                                     aRequest.role(), this);
454 }
455 
DoRequest(const BuildTransportRequest & aRequest)456 nsresult PresentationRequestParent::DoRequest(
457     const BuildTransportRequest& aRequest) {
458   MOZ_ASSERT(mService);
459 
460   // Validate the accessibility (primarily for receiver side) so that a
461   // compromised child process can't fake the ID.
462   if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
463                       ->IsSessionAccessible(aRequest.sessionId(),
464                                             aRequest.role(), OtherPid()))) {
465     return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
466   }
467 
468   nsresult rv = mService->BuildTransport(aRequest.sessionId(), aRequest.role());
469   if (NS_WARN_IF(NS_FAILED(rv))) {
470     return SendResponse(rv);
471   }
472   return SendResponse(NS_OK);
473 }
474 
475 NS_IMETHODIMP
NotifySuccess(const nsAString & aUrl)476 PresentationRequestParent::NotifySuccess(const nsAString& aUrl) {
477   Unused << SendNotifyRequestUrlSelected(nsString(aUrl));
478   return SendResponse(NS_OK);
479 }
480 
481 NS_IMETHODIMP
NotifyError(nsresult aError)482 PresentationRequestParent::NotifyError(nsresult aError) {
483   return SendResponse(aError);
484 }
485 
SendResponse(nsresult aResult)486 nsresult PresentationRequestParent::SendResponse(nsresult aResult) {
487   if (NS_WARN_IF(mActorDestroyed || !Send__delete__(this, aResult))) {
488     return NS_ERROR_FAILURE;
489   }
490 
491   return NS_OK;
492 }
493 
494 }  // namespace dom
495 }  // namespace mozilla
496