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