1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et ft=cpp : */
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 "mozilla/dom/ContentChild.h"
8 #include "mozilla/dom/PermissionMessageUtils.h"
9 #include "mozilla/dom/PPresentation.h"
10 #include "mozilla/dom/TabParent.h"
11 #include "mozilla/ipc/InputStreamUtils.h"
12 #include "mozilla/ipc/URIUtils.h"
13 #include "nsGlobalWindow.h"
14 #include "nsIPresentationListener.h"
15 #include "PresentationCallbacks.h"
16 #include "PresentationChild.h"
17 #include "PresentationContentSessionInfo.h"
18 #include "PresentationIPCService.h"
19 #include "PresentationLog.h"
20 
21 using namespace mozilla;
22 using namespace mozilla::dom;
23 using namespace mozilla::ipc;
24 
25 namespace {
26 
27 PresentationChild* sPresentationChild;
28 
29 } // anonymous
30 
NS_IMPL_ISUPPORTS(PresentationIPCService,nsIPresentationService,nsIPresentationAvailabilityListener)31 NS_IMPL_ISUPPORTS(PresentationIPCService,
32                   nsIPresentationService,
33                   nsIPresentationAvailabilityListener)
34 
35 PresentationIPCService::PresentationIPCService()
36 {
37   ContentChild* contentChild = ContentChild::GetSingleton();
38   if (NS_WARN_IF(!contentChild)) {
39     return;
40   }
41   sPresentationChild = new PresentationChild(this);
42   Unused <<
43     NS_WARN_IF(!contentChild->SendPPresentationConstructor(sPresentationChild));
44 }
45 
46 /* virtual */
~PresentationIPCService()47 PresentationIPCService::~PresentationIPCService()
48 {
49   Shutdown();
50 
51   mSessionListeners.Clear();
52   mSessionInfoAtController.Clear();
53   mSessionInfoAtReceiver.Clear();
54   sPresentationChild = nullptr;
55 }
56 
57 NS_IMETHODIMP
StartSession(const nsTArray<nsString> & aUrls,const nsAString & aSessionId,const nsAString & aOrigin,const nsAString & aDeviceId,uint64_t aWindowId,nsIDOMEventTarget * aEventTarget,nsIPrincipal * aPrincipal,nsIPresentationServiceCallback * aCallback,nsIPresentationTransportBuilderConstructor * aBuilderConstructor)58 PresentationIPCService::StartSession(
59                const nsTArray<nsString>& aUrls,
60                const nsAString& aSessionId,
61                const nsAString& aOrigin,
62                const nsAString& aDeviceId,
63                uint64_t aWindowId,
64                nsIDOMEventTarget* aEventTarget,
65                nsIPrincipal* aPrincipal,
66                nsIPresentationServiceCallback* aCallback,
67                nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
68 {
69   if (aWindowId != 0) {
70     AddRespondingSessionId(aWindowId,
71                            aSessionId,
72                            nsIPresentationService::ROLE_CONTROLLER);
73   }
74 
75   nsPIDOMWindowInner* window =
76     nsGlobalWindow::GetInnerWindowWithId(aWindowId)->AsInner();
77   TabId tabId = TabParent::GetTabIdFrom(window->GetDocShell());
78 
79   return SendRequest(aCallback, StartSessionRequest(aUrls,
80                                                     nsString(aSessionId),
81                                                     nsString(aOrigin),
82                                                     nsString(aDeviceId),
83                                                     aWindowId,
84                                                     tabId,
85                                                     IPC::Principal(aPrincipal)));
86 }
87 
88 NS_IMETHODIMP
SendSessionMessage(const nsAString & aSessionId,uint8_t aRole,const nsAString & aData)89 PresentationIPCService::SendSessionMessage(const nsAString& aSessionId,
90                                            uint8_t aRole,
91                                            const nsAString& aData)
92 {
93   MOZ_ASSERT(!aSessionId.IsEmpty());
94   MOZ_ASSERT(!aData.IsEmpty());
95 
96   RefPtr<PresentationContentSessionInfo> info =
97     GetSessionInfo(aSessionId, aRole);
98   // data channel session transport is maintained by content process
99   if (info) {
100     return info->Send(aData);
101   }
102 
103   return SendRequest(nullptr, SendSessionMessageRequest(nsString(aSessionId),
104                                                         aRole,
105                                                         nsString(aData)));
106 }
107 
108 NS_IMETHODIMP
SendSessionBinaryMsg(const nsAString & aSessionId,uint8_t aRole,const nsACString & aData)109 PresentationIPCService::SendSessionBinaryMsg(const nsAString& aSessionId,
110                                              uint8_t aRole,
111                                              const nsACString &aData)
112 {
113   MOZ_ASSERT(NS_IsMainThread());
114   MOZ_ASSERT(!aData.IsEmpty());
115   MOZ_ASSERT(!aSessionId.IsEmpty());
116   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
117              aRole == nsIPresentationService::ROLE_RECEIVER);
118 
119   RefPtr<PresentationContentSessionInfo> info =
120     GetSessionInfo(aSessionId, aRole);
121   // data channel session transport is maintained by content process
122   if (info) {
123     return info->SendBinaryMsg(aData);
124   }
125 
126   return NS_ERROR_NOT_AVAILABLE;
127 }
128 
129 NS_IMETHODIMP
SendSessionBlob(const nsAString & aSessionId,uint8_t aRole,nsIDOMBlob * aBlob)130 PresentationIPCService::SendSessionBlob(const nsAString& aSessionId,
131                                         uint8_t aRole,
132                                         nsIDOMBlob* aBlob)
133 {
134   MOZ_ASSERT(NS_IsMainThread());
135   MOZ_ASSERT(!aSessionId.IsEmpty());
136   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
137              aRole == nsIPresentationService::ROLE_RECEIVER);
138   MOZ_ASSERT(aBlob);
139 
140   RefPtr<PresentationContentSessionInfo> info =
141     GetSessionInfo(aSessionId, aRole);
142   // data channel session transport is maintained by content process
143   if (info) {
144     return info->SendBlob(aBlob);
145   }
146 
147   return NS_ERROR_NOT_AVAILABLE;
148 }
149 
150 NS_IMETHODIMP
CloseSession(const nsAString & aSessionId,uint8_t aRole,uint8_t aClosedReason)151 PresentationIPCService::CloseSession(const nsAString& aSessionId,
152                                      uint8_t aRole,
153                                      uint8_t aClosedReason)
154 {
155   MOZ_ASSERT(!aSessionId.IsEmpty());
156 
157   nsresult rv = SendRequest(nullptr, CloseSessionRequest(nsString(aSessionId),
158                                                          aRole,
159                                                          aClosedReason));
160   if (NS_WARN_IF(NS_FAILED(rv))) {
161     return rv;
162   }
163 
164   RefPtr<PresentationContentSessionInfo> info =
165     GetSessionInfo(aSessionId, aRole);
166   if (info) {
167     return info->Close(NS_OK);
168   }
169 
170   return NS_OK;
171 }
172 
173 NS_IMETHODIMP
TerminateSession(const nsAString & aSessionId,uint8_t aRole)174 PresentationIPCService::TerminateSession(const nsAString& aSessionId,
175                                          uint8_t aRole)
176 {
177   MOZ_ASSERT(!aSessionId.IsEmpty());
178 
179   nsresult rv = SendRequest(nullptr, TerminateSessionRequest(nsString(aSessionId), aRole));
180   if (NS_WARN_IF(NS_FAILED(rv))) {
181     return rv;
182   }
183 
184   RefPtr<PresentationContentSessionInfo> info =
185     GetSessionInfo(aSessionId, aRole);
186   if (info) {
187     return info->Close(NS_OK);
188   }
189 
190   return NS_OK;
191 }
192 
193 NS_IMETHODIMP
ReconnectSession(const nsTArray<nsString> & aUrls,const nsAString & aSessionId,uint8_t aRole,nsIPresentationServiceCallback * aCallback)194 PresentationIPCService::ReconnectSession(const nsTArray<nsString>& aUrls,
195                                          const nsAString& aSessionId,
196                                          uint8_t aRole,
197                                          nsIPresentationServiceCallback* aCallback)
198 {
199   MOZ_ASSERT(!aSessionId.IsEmpty());
200 
201   if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
202     MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
203     return NS_ERROR_INVALID_ARG;
204   }
205 
206   return SendRequest(aCallback, ReconnectSessionRequest(aUrls,
207                                                         nsString(aSessionId),
208                                                         aRole));
209 }
210 
211 NS_IMETHODIMP
BuildTransport(const nsAString & aSessionId,uint8_t aRole)212 PresentationIPCService::BuildTransport(const nsAString& aSessionId,
213                                        uint8_t aRole)
214 {
215   MOZ_ASSERT(!aSessionId.IsEmpty());
216 
217   if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
218     MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
219     return NS_ERROR_INVALID_ARG;
220   }
221 
222   return SendRequest(nullptr, BuildTransportRequest(nsString(aSessionId),
223                                                     aRole));
224 }
225 
226 nsresult
SendRequest(nsIPresentationServiceCallback * aCallback,const PresentationIPCRequest & aRequest)227 PresentationIPCService::SendRequest(nsIPresentationServiceCallback* aCallback,
228                                     const PresentationIPCRequest& aRequest)
229 {
230   if (sPresentationChild) {
231     PresentationRequestChild* actor = new PresentationRequestChild(aCallback);
232     Unused << NS_WARN_IF(!sPresentationChild->SendPPresentationRequestConstructor(actor, aRequest));
233   }
234   return NS_OK;
235 }
236 
237 NS_IMETHODIMP
RegisterAvailabilityListener(const nsTArray<nsString> & aAvailabilityUrls,nsIPresentationAvailabilityListener * aListener)238 PresentationIPCService::RegisterAvailabilityListener(
239                                 const nsTArray<nsString>& aAvailabilityUrls,
240                                 nsIPresentationAvailabilityListener* aListener)
241 {
242   MOZ_ASSERT(NS_IsMainThread());
243   MOZ_ASSERT(!aAvailabilityUrls.IsEmpty());
244   MOZ_ASSERT(aListener);
245 
246   nsTArray<nsString> addedUrls;
247   mAvailabilityManager.AddAvailabilityListener(aAvailabilityUrls,
248                                                aListener,
249                                                addedUrls);
250 
251   if (sPresentationChild && !addedUrls.IsEmpty()) {
252     Unused <<
253       NS_WARN_IF(
254         !sPresentationChild->SendRegisterAvailabilityHandler(addedUrls));
255   }
256   return NS_OK;
257 }
258 
259 NS_IMETHODIMP
UnregisterAvailabilityListener(const nsTArray<nsString> & aAvailabilityUrls,nsIPresentationAvailabilityListener * aListener)260 PresentationIPCService::UnregisterAvailabilityListener(
261                                 const nsTArray<nsString>& aAvailabilityUrls,
262                                 nsIPresentationAvailabilityListener* aListener)
263 {
264   MOZ_ASSERT(NS_IsMainThread());
265 
266   nsTArray<nsString> removedUrls;
267   mAvailabilityManager.RemoveAvailabilityListener(aAvailabilityUrls,
268                                                   aListener,
269                                                   removedUrls);
270 
271   if (sPresentationChild && !removedUrls.IsEmpty()) {
272     Unused <<
273       NS_WARN_IF(
274         !sPresentationChild->SendUnregisterAvailabilityHandler(removedUrls));
275   }
276   return NS_OK;
277 }
278 
279 NS_IMETHODIMP
RegisterSessionListener(const nsAString & aSessionId,uint8_t aRole,nsIPresentationSessionListener * aListener)280 PresentationIPCService::RegisterSessionListener(const nsAString& aSessionId,
281                                                 uint8_t aRole,
282                                                 nsIPresentationSessionListener* aListener)
283 {
284   MOZ_ASSERT(NS_IsMainThread());
285   MOZ_ASSERT(aListener);
286 
287   nsCOMPtr<nsIPresentationSessionListener> listener;
288   if (mSessionListeners.Get(aSessionId, getter_AddRefs(listener))) {
289     mSessionListeners.Put(aSessionId, aListener);
290     return NS_OK;
291   }
292 
293   mSessionListeners.Put(aSessionId, aListener);
294   if (sPresentationChild) {
295     Unused <<
296       NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler(
297         nsString(aSessionId), aRole));
298   }
299   return NS_OK;
300 }
301 
302 NS_IMETHODIMP
UnregisterSessionListener(const nsAString & aSessionId,uint8_t aRole)303 PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId,
304                                                   uint8_t aRole)
305 {
306   MOZ_ASSERT(NS_IsMainThread());
307 
308   UntrackSessionInfo(aSessionId, aRole);
309 
310   mSessionListeners.Remove(aSessionId);
311   if (sPresentationChild) {
312     Unused <<
313       NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler(
314         nsString(aSessionId), aRole));
315   }
316   return NS_OK;
317 }
318 
319 NS_IMETHODIMP
RegisterRespondingListener(uint64_t aWindowId,nsIPresentationRespondingListener * aListener)320 PresentationIPCService::RegisterRespondingListener(uint64_t aWindowId,
321                                                    nsIPresentationRespondingListener* aListener)
322 {
323   MOZ_ASSERT(NS_IsMainThread());
324 
325   mRespondingListeners.Put(aWindowId, aListener);
326   if (sPresentationChild) {
327     Unused <<
328       NS_WARN_IF(!sPresentationChild->SendRegisterRespondingHandler(aWindowId));
329   }
330   return NS_OK;
331 }
332 
333 NS_IMETHODIMP
UnregisterRespondingListener(uint64_t aWindowId)334 PresentationIPCService::UnregisterRespondingListener(uint64_t aWindowId)
335 {
336   MOZ_ASSERT(NS_IsMainThread());
337 
338   mRespondingListeners.Remove(aWindowId);
339   if (sPresentationChild) {
340     Unused <<
341       NS_WARN_IF(!sPresentationChild->SendUnregisterRespondingHandler(
342         aWindowId));
343   }
344   return NS_OK;
345 }
346 
347 nsresult
NotifySessionTransport(const nsString & aSessionId,const uint8_t & aRole,nsIPresentationSessionTransport * aTransport)348 PresentationIPCService::NotifySessionTransport(const nsString& aSessionId,
349                                                const uint8_t& aRole,
350                                                nsIPresentationSessionTransport* aTransport)
351 {
352   RefPtr<PresentationContentSessionInfo> info =
353     new PresentationContentSessionInfo(aSessionId, aRole, aTransport);
354 
355   if (NS_WARN_IF(NS_FAILED(info->Init()))) {
356     return NS_ERROR_NOT_AVAILABLE;
357   }
358 
359   if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
360     mSessionInfoAtController.Put(aSessionId, info);
361   } else {
362     mSessionInfoAtReceiver.Put(aSessionId, info);
363   }
364   return NS_OK;
365 }
366 
367 NS_IMETHODIMP
GetWindowIdBySessionId(const nsAString & aSessionId,uint8_t aRole,uint64_t * aWindowId)368 PresentationIPCService::GetWindowIdBySessionId(const nsAString& aSessionId,
369                                                uint8_t aRole,
370                                                uint64_t* aWindowId)
371 {
372   return GetWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId);
373 }
374 
375 NS_IMETHODIMP
UpdateWindowIdBySessionId(const nsAString & aSessionId,uint8_t aRole,const uint64_t aWindowId)376 PresentationIPCService::UpdateWindowIdBySessionId(const nsAString& aSessionId,
377                                                   uint8_t aRole,
378                                                   const uint64_t aWindowId)
379 {
380   return UpdateWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId);
381 }
382 
383 nsresult
NotifySessionStateChange(const nsAString & aSessionId,uint16_t aState,nsresult aReason)384 PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId,
385                                                  uint16_t aState,
386                                                  nsresult aReason)
387 {
388   nsCOMPtr<nsIPresentationSessionListener> listener;
389   if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
390     return NS_OK;
391   }
392 
393   return listener->NotifyStateChange(aSessionId, aState, aReason);
394 }
395 
396 // Only used for OOP RTCDataChannel session transport case.
397 nsresult
NotifyMessage(const nsAString & aSessionId,const nsACString & aData,const bool & aIsBinary)398 PresentationIPCService::NotifyMessage(const nsAString& aSessionId,
399                                       const nsACString& aData,
400                                       const bool& aIsBinary)
401 {
402   nsCOMPtr<nsIPresentationSessionListener> listener;
403   if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
404     return NS_OK;
405   }
406 
407   return listener->NotifyMessage(aSessionId, aData, aIsBinary);
408 }
409 
410 // Only used for OOP RTCDataChannel session transport case.
411 nsresult
NotifyTransportClosed(const nsAString & aSessionId,uint8_t aRole,nsresult aReason)412 PresentationIPCService::NotifyTransportClosed(const nsAString& aSessionId,
413                                               uint8_t aRole,
414                                               nsresult aReason)
415 {
416   RefPtr<PresentationContentSessionInfo> info =
417     GetSessionInfo(aSessionId, aRole);
418   if (NS_WARN_IF(!info)) {
419     return NS_ERROR_NOT_AVAILABLE;
420   }
421   Unused << NS_WARN_IF(!sPresentationChild->SendNotifyTransportClosed(nsString(aSessionId), aRole, aReason));
422   return NS_OK;
423 }
424 
425 nsresult
NotifySessionConnect(uint64_t aWindowId,const nsAString & aSessionId)426 PresentationIPCService::NotifySessionConnect(uint64_t aWindowId,
427                                              const nsAString& aSessionId)
428 {
429   nsCOMPtr<nsIPresentationRespondingListener> listener;
430   if (NS_WARN_IF(!mRespondingListeners.Get(aWindowId, getter_AddRefs(listener)))) {
431     return NS_OK;
432   }
433 
434   return listener->NotifySessionConnect(aWindowId, aSessionId);
435 }
436 
437 NS_IMETHODIMP
NotifyAvailableChange(const nsTArray<nsString> & aAvailabilityUrls,bool aAvailable)438 PresentationIPCService::NotifyAvailableChange(
439                                    const nsTArray<nsString>& aAvailabilityUrls,
440                                    bool aAvailable)
441 {
442   return mAvailabilityManager.DoNotifyAvailableChange(aAvailabilityUrls,
443                                                       aAvailable);
444 }
445 
446 NS_IMETHODIMP
NotifyReceiverReady(const nsAString & aSessionId,uint64_t aWindowId,bool aIsLoading,nsIPresentationTransportBuilderConstructor * aBuilderConstructor)447 PresentationIPCService::NotifyReceiverReady(
448                const nsAString& aSessionId,
449                uint64_t aWindowId,
450                bool aIsLoading,
451                nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
452 {
453   MOZ_ASSERT(NS_IsMainThread());
454 
455   // No actual window uses 0 as its ID.
456   if (NS_WARN_IF(aWindowId == 0)) {
457     return NS_ERROR_NOT_AVAILABLE;
458   }
459 
460   // Track the responding info for an OOP receiver page.
461   AddRespondingSessionId(aWindowId,
462                          aSessionId,
463                          nsIPresentationService::ROLE_RECEIVER);
464 
465   Unused << NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsString(aSessionId),
466                                                                     aWindowId,
467                                                                     aIsLoading));
468 
469   // Release mCallback after using aSessionId
470   // because aSessionId is held by mCallback.
471   mCallback = nullptr;
472   return NS_OK;
473 }
474 
475 NS_IMETHODIMP
UntrackSessionInfo(const nsAString & aSessionId,uint8_t aRole)476 PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId,
477                                            uint8_t aRole)
478 {
479   PRES_DEBUG("content %s:id[%s], role[%d]\n", __func__,
480              NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
481 
482   if (nsIPresentationService::ROLE_RECEIVER == aRole) {
483     // Terminate receiver page.
484     uint64_t windowId;
485     if (NS_SUCCEEDED(GetWindowIdBySessionIdInternal(aSessionId,
486                                                     aRole,
487                                                     &windowId))) {
488       NS_DispatchToMainThread(NS_NewRunnableFunction([windowId]() -> void {
489         PRES_DEBUG("Attempt to close window[%d]\n", windowId);
490 
491         if (auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId)) {
492           window->Close();
493         }
494       }));
495     }
496   }
497 
498   // Remove the OOP responding info (if it has never been used).
499   RemoveRespondingSessionId(aSessionId, aRole);
500 
501   if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
502     mSessionInfoAtController.Remove(aSessionId);
503   } else {
504     mSessionInfoAtReceiver.Remove(aSessionId);
505   }
506 
507   return NS_OK;
508 }
509 
510 void
NotifyPresentationChildDestroyed()511 PresentationIPCService::NotifyPresentationChildDestroyed()
512 {
513   sPresentationChild = nullptr;
514 }
515 
516 nsresult
MonitorResponderLoading(const nsAString & aSessionId,nsIDocShell * aDocShell)517 PresentationIPCService::MonitorResponderLoading(const nsAString& aSessionId,
518                                                 nsIDocShell* aDocShell)
519 {
520   MOZ_ASSERT(NS_IsMainThread());
521 
522   mCallback = new PresentationResponderLoadingCallback(aSessionId);
523   return mCallback->Init(aDocShell);
524 }
525 
526 nsresult
CloseContentSessionTransport(const nsString & aSessionId,uint8_t aRole,nsresult aReason)527 PresentationIPCService::CloseContentSessionTransport(const nsString& aSessionId,
528                                                      uint8_t aRole,
529                                                      nsresult aReason)
530 {
531   RefPtr<PresentationContentSessionInfo> info =
532     GetSessionInfo(aSessionId, aRole);
533   if (NS_WARN_IF(!info)) {
534     return NS_ERROR_NOT_AVAILABLE;
535   }
536 
537   return info->Close(aReason);
538 }
539