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
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "PresentationService.h"
8 
9 #include "ipc/PresentationIPCService.h"
10 #include "mozilla/Services.h"
11 #include "nsGlobalWindow.h"
12 #include "nsIMutableArray.h"
13 #include "nsIObserverService.h"
14 #include "nsIPresentationControlChannel.h"
15 #include "nsIPresentationDeviceManager.h"
16 #include "nsIPresentationDevicePrompt.h"
17 #include "nsIPresentationListener.h"
18 #include "nsIPresentationRequestUIGlue.h"
19 #include "nsIPresentationSessionRequest.h"
20 #include "nsIPresentationTerminateRequest.h"
21 #include "nsISupportsPrimitives.h"
22 #include "nsNetUtil.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsThreadUtils.h"
25 #include "nsXPCOMCID.h"
26 #include "nsXULAppAPI.h"
27 #include "PresentationLog.h"
28 
29 namespace mozilla {
30 namespace dom {
31 
IsSameDevice(nsIPresentationDevice * aDevice,nsIPresentationDevice * aDeviceAnother)32 static bool IsSameDevice(nsIPresentationDevice* aDevice,
33                          nsIPresentationDevice* aDeviceAnother) {
34   if (!aDevice || !aDeviceAnother) {
35     return false;
36   }
37 
38   nsAutoCString deviceId;
39   aDevice->GetId(deviceId);
40   nsAutoCString anotherId;
41   aDeviceAnother->GetId(anotherId);
42   if (!deviceId.Equals(anotherId)) {
43     return false;
44   }
45 
46   nsAutoCString deviceType;
47   aDevice->GetType(deviceType);
48   nsAutoCString anotherType;
49   aDeviceAnother->GetType(anotherType);
50   if (!deviceType.Equals(anotherType)) {
51     return false;
52   }
53 
54   return true;
55 }
56 
ConvertURLArrayHelper(const nsTArray<nsString> & aUrls,nsIArray ** aResult)57 static nsresult ConvertURLArrayHelper(const nsTArray<nsString>& aUrls,
58                                       nsIArray** aResult) {
59   if (!aResult) {
60     return NS_ERROR_INVALID_POINTER;
61   }
62 
63   *aResult = nullptr;
64 
65   nsresult rv;
66   nsCOMPtr<nsIMutableArray> urls = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
67   if (NS_WARN_IF(NS_FAILED(rv))) {
68     return rv;
69   }
70 
71   for (const auto& url : aUrls) {
72     nsCOMPtr<nsISupportsString> isupportsString =
73         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
74     if (NS_WARN_IF(NS_FAILED(rv))) {
75       return rv;
76     }
77 
78     rv = isupportsString->SetData(url);
79     if (NS_WARN_IF(NS_FAILED(rv))) {
80       return rv;
81     }
82 
83     rv = urls->AppendElement(isupportsString);
84     if (NS_WARN_IF(NS_FAILED(rv))) {
85       return rv;
86     }
87   }
88 
89   urls.forget(aResult);
90   return NS_OK;
91 }
92 
93 /*
94  * Implementation of PresentationDeviceRequest
95  */
96 
97 class PresentationDeviceRequest final : public nsIPresentationDeviceRequest {
98  public:
99   NS_DECL_ISUPPORTS
100   NS_DECL_NSIPRESENTATIONDEVICEREQUEST
101 
102   PresentationDeviceRequest(
103       const nsTArray<nsString>& aUrls, const nsAString& aId,
104       const nsAString& aOrigin, uint64_t aWindowId,
105       nsIDOMEventTarget* aEventTarget, nsIPrincipal* aPrincipal,
106       nsIPresentationServiceCallback* aCallback,
107       nsIPresentationTransportBuilderConstructor* aBuilderConstructor);
108 
109  private:
110   virtual ~PresentationDeviceRequest() = default;
111   nsresult CreateSessionInfo(nsIPresentationDevice* aDevice,
112                              const nsAString& aSelectedRequestUrl);
113 
114   nsTArray<nsString> mRequestUrls;
115   nsString mId;
116   nsString mOrigin;
117   uint64_t mWindowId;
118   nsWeakPtr mChromeEventHandler;
119   nsCOMPtr<nsIPrincipal> mPrincipal;
120   nsCOMPtr<nsIPresentationServiceCallback> mCallback;
121   nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor;
122 };
123 
124 LazyLogModule gPresentationLog("Presentation");
125 
NS_IMPL_ISUPPORTS(PresentationDeviceRequest,nsIPresentationDeviceRequest)126 NS_IMPL_ISUPPORTS(PresentationDeviceRequest, nsIPresentationDeviceRequest)
127 
128 PresentationDeviceRequest::PresentationDeviceRequest(
129     const nsTArray<nsString>& aUrls, const nsAString& aId,
130     const nsAString& aOrigin, uint64_t aWindowId,
131     nsIDOMEventTarget* aEventTarget, nsIPrincipal* aPrincipal,
132     nsIPresentationServiceCallback* aCallback,
133     nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
134     : mRequestUrls(aUrls),
135       mId(aId),
136       mOrigin(aOrigin),
137       mWindowId(aWindowId),
138       mChromeEventHandler(do_GetWeakReference(aEventTarget)),
139       mPrincipal(aPrincipal),
140       mCallback(aCallback),
141       mBuilderConstructor(aBuilderConstructor) {
142   MOZ_ASSERT(!mRequestUrls.IsEmpty());
143   MOZ_ASSERT(!mId.IsEmpty());
144   MOZ_ASSERT(!mOrigin.IsEmpty());
145   MOZ_ASSERT(mCallback);
146   MOZ_ASSERT(mBuilderConstructor);
147 }
148 
149 NS_IMETHODIMP
GetOrigin(nsAString & aOrigin)150 PresentationDeviceRequest::GetOrigin(nsAString& aOrigin) {
151   aOrigin = mOrigin;
152   return NS_OK;
153 }
154 
155 NS_IMETHODIMP
GetRequestURLs(nsIArray ** aUrls)156 PresentationDeviceRequest::GetRequestURLs(nsIArray** aUrls) {
157   return ConvertURLArrayHelper(mRequestUrls, aUrls);
158 }
159 
160 NS_IMETHODIMP
GetChromeEventHandler(nsIDOMEventTarget ** aChromeEventHandler)161 PresentationDeviceRequest::GetChromeEventHandler(
162     nsIDOMEventTarget** aChromeEventHandler) {
163   nsCOMPtr<nsIDOMEventTarget> handler(do_QueryReferent(mChromeEventHandler));
164   handler.forget(aChromeEventHandler);
165   return NS_OK;
166 }
167 
168 NS_IMETHODIMP
GetPrincipal(nsIPrincipal ** aPrincipal)169 PresentationDeviceRequest::GetPrincipal(nsIPrincipal** aPrincipal) {
170   nsCOMPtr<nsIPrincipal> principal(mPrincipal);
171   principal.forget(aPrincipal);
172   return NS_OK;
173 }
174 
175 NS_IMETHODIMP
Select(nsIPresentationDevice * aDevice)176 PresentationDeviceRequest::Select(nsIPresentationDevice* aDevice) {
177   MOZ_ASSERT(NS_IsMainThread());
178   if (NS_WARN_IF(!aDevice)) {
179     MOZ_ASSERT(false, "|aDevice| should noe be null.");
180     mCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
181     return NS_ERROR_INVALID_ARG;
182   }
183 
184   // Select the most suitable URL for starting the presentation.
185   nsAutoString selectedRequestUrl;
186   for (const auto& url : mRequestUrls) {
187     bool isSupported;
188     if (NS_SUCCEEDED(aDevice->IsRequestedUrlSupported(url, &isSupported)) &&
189         isSupported) {
190       selectedRequestUrl.Assign(url);
191       break;
192     }
193   }
194 
195   if (selectedRequestUrl.IsEmpty()) {
196     return mCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
197   }
198 
199   if (NS_WARN_IF(NS_FAILED(CreateSessionInfo(aDevice, selectedRequestUrl)))) {
200     return mCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
201   }
202 
203   return mCallback->NotifySuccess(selectedRequestUrl);
204 }
205 
CreateSessionInfo(nsIPresentationDevice * aDevice,const nsAString & aSelectedRequestUrl)206 nsresult PresentationDeviceRequest::CreateSessionInfo(
207     nsIPresentationDevice* aDevice, const nsAString& aSelectedRequestUrl) {
208   nsCOMPtr<nsIPresentationService> service =
209       do_GetService(PRESENTATION_SERVICE_CONTRACTID);
210   if (NS_WARN_IF(!service)) {
211     return NS_ERROR_NOT_AVAILABLE;
212   }
213 
214   // Create the controlling session info
215   RefPtr<PresentationSessionInfo> info =
216       static_cast<PresentationService*>(service.get())
217           ->CreateControllingSessionInfo(aSelectedRequestUrl, mId, mWindowId);
218   if (NS_WARN_IF(!info)) {
219     return NS_ERROR_NOT_AVAILABLE;
220   }
221   info->SetDevice(aDevice);
222 
223   // Establish a control channel. If we failed to do so, the callback is called
224   // with an error message.
225   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
226   nsresult rv = aDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel));
227   if (NS_WARN_IF(NS_FAILED(rv))) {
228     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
229   }
230 
231   // Initialize the session info with the control channel.
232   rv = info->Init(ctrlChannel);
233   if (NS_WARN_IF(NS_FAILED(rv))) {
234     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
235   }
236 
237   info->SetTransportBuilderConstructor(mBuilderConstructor);
238   return NS_OK;
239 }
240 
241 NS_IMETHODIMP
Cancel(nsresult aReason)242 PresentationDeviceRequest::Cancel(nsresult aReason) {
243   return mCallback->NotifyError(aReason);
244 }
245 
246 /*
247  * Implementation of PresentationService
248  */
249 
NS_IMPL_ISUPPORTS(PresentationService,nsIPresentationService,nsIObserver)250 NS_IMPL_ISUPPORTS(PresentationService, nsIPresentationService, nsIObserver)
251 
252 PresentationService::PresentationService() {}
253 
~PresentationService()254 PresentationService::~PresentationService() { HandleShutdown(); }
255 
Init()256 bool PresentationService::Init() {
257   MOZ_ASSERT(NS_IsMainThread());
258 
259   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
260   if (NS_WARN_IF(!obs)) {
261     return false;
262   }
263 
264   nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
265   if (NS_WARN_IF(NS_FAILED(rv))) {
266     return false;
267   }
268   rv = obs->AddObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC, false);
269   if (NS_WARN_IF(NS_FAILED(rv))) {
270     return false;
271   }
272   rv = obs->AddObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC, false);
273   if (NS_WARN_IF(NS_FAILED(rv))) {
274     return false;
275   }
276   rv = obs->AddObserver(this, PRESENTATION_TERMINATE_REQUEST_TOPIC, false);
277   if (NS_WARN_IF(NS_FAILED(rv))) {
278     return false;
279   }
280   rv = obs->AddObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC, false);
281   if (NS_WARN_IF(NS_FAILED(rv))) {
282     return false;
283   }
284 
285   return !NS_WARN_IF(NS_FAILED(rv));
286 }
287 
288 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)289 PresentationService::Observe(nsISupports* aSubject, const char* aTopic,
290                              const char16_t* aData) {
291   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
292     HandleShutdown();
293     return NS_OK;
294   } else if (!strcmp(aTopic, PRESENTATION_DEVICE_CHANGE_TOPIC)) {
295     // Ignore the "update" case here, since we only care about the arrival and
296     // removal of the device.
297     if (!NS_strcmp(aData, u"add")) {
298       nsCOMPtr<nsIPresentationDevice> device = do_QueryInterface(aSubject);
299       if (NS_WARN_IF(!device)) {
300         return NS_ERROR_FAILURE;
301       }
302 
303       return HandleDeviceAdded(device);
304     } else if (!NS_strcmp(aData, u"remove")) {
305       return HandleDeviceRemoved();
306     }
307 
308     return NS_OK;
309   } else if (!strcmp(aTopic, PRESENTATION_SESSION_REQUEST_TOPIC)) {
310     nsCOMPtr<nsIPresentationSessionRequest> request(
311         do_QueryInterface(aSubject));
312     if (NS_WARN_IF(!request)) {
313       return NS_ERROR_FAILURE;
314     }
315 
316     return HandleSessionRequest(request);
317   } else if (!strcmp(aTopic, PRESENTATION_TERMINATE_REQUEST_TOPIC)) {
318     nsCOMPtr<nsIPresentationTerminateRequest> request(
319         do_QueryInterface(aSubject));
320     if (NS_WARN_IF(!request)) {
321       return NS_ERROR_FAILURE;
322     }
323 
324     return HandleTerminateRequest(request);
325   } else if (!strcmp(aTopic, PRESENTATION_RECONNECT_REQUEST_TOPIC)) {
326     nsCOMPtr<nsIPresentationSessionRequest> request(
327         do_QueryInterface(aSubject));
328     if (NS_WARN_IF(!request)) {
329       return NS_ERROR_FAILURE;
330     }
331 
332     return HandleReconnectRequest(request);
333   } else if (!strcmp(aTopic, "profile-after-change")) {
334     // It's expected since we add and entry to |kLayoutCategories| in
335     // |nsLayoutModule.cpp| to launch this service earlier.
336     return NS_OK;
337   }
338 
339   MOZ_ASSERT(false, "Unexpected topic for PresentationService");
340   return NS_ERROR_UNEXPECTED;
341 }
342 
HandleShutdown()343 void PresentationService::HandleShutdown() {
344   MOZ_ASSERT(NS_IsMainThread());
345 
346   Shutdown();
347 
348   mAvailabilityManager.Clear();
349   mSessionInfoAtController.Clear();
350   mSessionInfoAtReceiver.Clear();
351 
352   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
353   if (obs) {
354     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
355     obs->RemoveObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC);
356     obs->RemoveObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC);
357     obs->RemoveObserver(this, PRESENTATION_TERMINATE_REQUEST_TOPIC);
358     obs->RemoveObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC);
359   }
360 }
361 
HandleDeviceAdded(nsIPresentationDevice * aDevice)362 nsresult PresentationService::HandleDeviceAdded(
363     nsIPresentationDevice* aDevice) {
364   PRES_DEBUG("%s\n", __func__);
365   if (!aDevice) {
366     MOZ_ASSERT(false, "aDevice shoud no be null.");
367     return NS_ERROR_INVALID_ARG;
368   }
369 
370   // Query for only unavailable URLs while device added.
371   nsTArray<nsString> unavailableUrls;
372   mAvailabilityManager.GetAvailbilityUrlByAvailability(unavailableUrls, false);
373 
374   nsTArray<nsString> supportedAvailabilityUrl;
375   for (const auto& url : unavailableUrls) {
376     bool isSupported;
377     if (NS_SUCCEEDED(aDevice->IsRequestedUrlSupported(url, &isSupported)) &&
378         isSupported) {
379       supportedAvailabilityUrl.AppendElement(url);
380     }
381   }
382 
383   if (!supportedAvailabilityUrl.IsEmpty()) {
384     return mAvailabilityManager.DoNotifyAvailableChange(
385         supportedAvailabilityUrl, true);
386   }
387 
388   return NS_OK;
389 }
390 
HandleDeviceRemoved()391 nsresult PresentationService::HandleDeviceRemoved() {
392   PRES_DEBUG("%s\n", __func__);
393 
394   // Query for only available URLs while device removed.
395   nsTArray<nsString> availabilityUrls;
396   mAvailabilityManager.GetAvailbilityUrlByAvailability(availabilityUrls, true);
397 
398   return UpdateAvailabilityUrlChange(availabilityUrls);
399 }
400 
UpdateAvailabilityUrlChange(const nsTArray<nsString> & aAvailabilityUrls)401 nsresult PresentationService::UpdateAvailabilityUrlChange(
402     const nsTArray<nsString>& aAvailabilityUrls) {
403   nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
404       do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
405   if (NS_WARN_IF(!deviceManager)) {
406     return NS_ERROR_NOT_AVAILABLE;
407   }
408 
409   nsCOMPtr<nsIArray> devices;
410   nsresult rv =
411       deviceManager->GetAvailableDevices(nullptr, getter_AddRefs(devices));
412   if (NS_WARN_IF(NS_FAILED(rv))) {
413     return rv;
414   }
415 
416   uint32_t numOfDevices;
417   devices->GetLength(&numOfDevices);
418 
419   nsTArray<nsString> supportedAvailabilityUrl;
420   for (const auto& url : aAvailabilityUrls) {
421     for (uint32_t i = 0; i < numOfDevices; ++i) {
422       nsCOMPtr<nsIPresentationDevice> device = do_QueryElementAt(devices, i);
423       if (device) {
424         bool isSupported;
425         if (NS_SUCCEEDED(device->IsRequestedUrlSupported(url, &isSupported)) &&
426             isSupported) {
427           supportedAvailabilityUrl.AppendElement(url);
428           break;
429         }
430       }
431     }
432   }
433 
434   if (supportedAvailabilityUrl.IsEmpty()) {
435     return mAvailabilityManager.DoNotifyAvailableChange(aAvailabilityUrls,
436                                                         false);
437   }
438 
439   return mAvailabilityManager.DoNotifyAvailableChange(supportedAvailabilityUrl,
440                                                       true);
441 }
442 
HandleSessionRequest(nsIPresentationSessionRequest * aRequest)443 nsresult PresentationService::HandleSessionRequest(
444     nsIPresentationSessionRequest* aRequest) {
445   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
446   nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
447   if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
448     return rv;
449   }
450 
451   nsAutoString url;
452   rv = aRequest->GetUrl(url);
453   if (NS_WARN_IF(NS_FAILED(rv))) {
454     ctrlChannel->Disconnect(rv);
455     return rv;
456   }
457 
458   nsAutoString sessionId;
459   rv = aRequest->GetPresentationId(sessionId);
460   if (NS_WARN_IF(NS_FAILED(rv))) {
461     ctrlChannel->Disconnect(rv);
462     return rv;
463   }
464 
465   nsCOMPtr<nsIPresentationDevice> device;
466   rv = aRequest->GetDevice(getter_AddRefs(device));
467   if (NS_WARN_IF(NS_FAILED(rv))) {
468     ctrlChannel->Disconnect(rv);
469     return rv;
470   }
471 
472   // Create or reuse session info.
473   RefPtr<PresentationSessionInfo> info =
474       GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER);
475 
476   // This is the case for reconnecting a session.
477   // Update the control channel and device of the session info.
478   // Call |NotifyResponderReady| to indicate the receiver page is already there.
479   if (info) {
480     PRES_DEBUG("handle reconnection:id[%s]\n",
481                NS_ConvertUTF16toUTF8(sessionId).get());
482 
483     info->SetControlChannel(ctrlChannel);
484     info->SetDevice(device);
485     return static_cast<PresentationPresentingInfo*>(info.get())->DoReconnect();
486   }
487 
488   // This is the case for a new session.
489   PRES_DEBUG("handle new session:url[%s], id[%s]\n",
490              NS_ConvertUTF16toUTF8(url).get(),
491              NS_ConvertUTF16toUTF8(sessionId).get());
492 
493   info = new PresentationPresentingInfo(url, sessionId, device);
494   rv = info->Init(ctrlChannel);
495   if (NS_WARN_IF(NS_FAILED(rv))) {
496     ctrlChannel->Disconnect(rv);
497     return rv;
498   }
499 
500   mSessionInfoAtReceiver.Put(sessionId, info);
501 
502   // Notify the receiver to launch.
503   nsCOMPtr<nsIPresentationRequestUIGlue> glue =
504       do_CreateInstance(PRESENTATION_REQUEST_UI_GLUE_CONTRACTID);
505   if (NS_WARN_IF(!glue)) {
506     ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
507     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
508   }
509   nsCOMPtr<nsISupports> promise;
510   rv = glue->SendRequest(url, sessionId, device, getter_AddRefs(promise));
511   if (NS_WARN_IF(NS_FAILED(rv))) {
512     ctrlChannel->Disconnect(rv);
513     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
514   }
515   nsCOMPtr<Promise> realPromise = do_QueryInterface(promise);
516   static_cast<PresentationPresentingInfo*>(info.get())->SetPromise(realPromise);
517 
518   return NS_OK;
519 }
520 
HandleTerminateRequest(nsIPresentationTerminateRequest * aRequest)521 nsresult PresentationService::HandleTerminateRequest(
522     nsIPresentationTerminateRequest* aRequest) {
523   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
524   nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
525   if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
526     return rv;
527   }
528 
529   nsAutoString sessionId;
530   rv = aRequest->GetPresentationId(sessionId);
531   if (NS_WARN_IF(NS_FAILED(rv))) {
532     ctrlChannel->Disconnect(rv);
533     return rv;
534   }
535 
536   nsCOMPtr<nsIPresentationDevice> device;
537   rv = aRequest->GetDevice(getter_AddRefs(device));
538   if (NS_WARN_IF(NS_FAILED(rv))) {
539     ctrlChannel->Disconnect(rv);
540     return rv;
541   }
542 
543   bool isFromReceiver;
544   rv = aRequest->GetIsFromReceiver(&isFromReceiver);
545   if (NS_WARN_IF(NS_FAILED(rv))) {
546     ctrlChannel->Disconnect(rv);
547     return rv;
548   }
549 
550   RefPtr<PresentationSessionInfo> info;
551   if (!isFromReceiver) {
552     info = GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER);
553   } else {
554     info = GetSessionInfo(sessionId, nsIPresentationService::ROLE_CONTROLLER);
555   }
556   if (NS_WARN_IF(!info)) {
557     // Cannot terminate non-existed session.
558     ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
559     return NS_ERROR_DOM_ABORT_ERR;
560   }
561 
562   // Check if terminate request comes from known device.
563   RefPtr<nsIPresentationDevice> knownDevice = info->GetDevice();
564   if (NS_WARN_IF(!IsSameDevice(device, knownDevice))) {
565     ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
566     return NS_ERROR_DOM_ABORT_ERR;
567   }
568 
569   PRES_DEBUG("%s:handle termination:id[%s], receiver[%d]\n", __func__,
570              NS_ConvertUTF16toUTF8(sessionId).get(), isFromReceiver);
571 
572   return info->OnTerminate(ctrlChannel);
573 }
574 
HandleReconnectRequest(nsIPresentationSessionRequest * aRequest)575 nsresult PresentationService::HandleReconnectRequest(
576     nsIPresentationSessionRequest* aRequest) {
577   nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
578   nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
579   if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
580     return rv;
581   }
582 
583   nsAutoString sessionId;
584   rv = aRequest->GetPresentationId(sessionId);
585   if (NS_WARN_IF(NS_FAILED(rv))) {
586     ctrlChannel->Disconnect(rv);
587     return rv;
588   }
589 
590   uint64_t windowId;
591   rv = GetWindowIdBySessionIdInternal(
592       sessionId, nsIPresentationService::ROLE_RECEIVER, &windowId);
593   if (NS_WARN_IF(NS_FAILED(rv))) {
594     ctrlChannel->Disconnect(rv);
595     return rv;
596   }
597 
598   RefPtr<PresentationSessionInfo> info =
599       GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER);
600   if (NS_WARN_IF(!info)) {
601     // Cannot reconnect non-existed session
602     ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
603     return NS_ERROR_DOM_ABORT_ERR;
604   }
605 
606   nsAutoString url;
607   rv = aRequest->GetUrl(url);
608   if (NS_WARN_IF(NS_FAILED(rv))) {
609     ctrlChannel->Disconnect(rv);
610     return rv;
611   }
612 
613   // Make sure the url is the same as the previous one.
614   if (NS_WARN_IF(!info->GetUrl().Equals(url))) {
615     ctrlChannel->Disconnect(rv);
616     return rv;
617   }
618 
619   return HandleSessionRequest(aRequest);
620 }
621 
622 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)623 PresentationService::StartSession(
624     const nsTArray<nsString>& aUrls, const nsAString& aSessionId,
625     const nsAString& aOrigin, const nsAString& aDeviceId, uint64_t aWindowId,
626     nsIDOMEventTarget* aEventTarget, nsIPrincipal* aPrincipal,
627     nsIPresentationServiceCallback* aCallback,
628     nsIPresentationTransportBuilderConstructor* aBuilderConstructor) {
629   PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get());
630 
631   MOZ_ASSERT(NS_IsMainThread());
632   MOZ_ASSERT(aCallback);
633   MOZ_ASSERT(!aSessionId.IsEmpty());
634   MOZ_ASSERT(!aUrls.IsEmpty());
635 
636   nsCOMPtr<nsIPresentationDeviceRequest> request =
637       new PresentationDeviceRequest(aUrls, aSessionId, aOrigin, aWindowId,
638                                     aEventTarget, aPrincipal, aCallback,
639                                     aBuilderConstructor);
640 
641   if (aDeviceId.IsVoid()) {
642     // Pop up a prompt and ask user to select a device.
643     nsCOMPtr<nsIPresentationDevicePrompt> prompt =
644         do_GetService(PRESENTATION_DEVICE_PROMPT_CONTRACTID);
645     if (NS_WARN_IF(!prompt)) {
646       return aCallback->NotifyError(NS_ERROR_DOM_INVALID_ACCESS_ERR);
647     }
648 
649     nsresult rv = prompt->PromptDeviceSelection(request);
650     if (NS_WARN_IF(NS_FAILED(rv))) {
651       return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
652     }
653 
654     return NS_OK;
655   }
656 
657   // Find the designated device from available device list.
658   nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
659       do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
660   if (NS_WARN_IF(!deviceManager)) {
661     return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
662   }
663 
664   nsCOMPtr<nsIArray> presentationUrls;
665   if (NS_WARN_IF(NS_FAILED(
666           ConvertURLArrayHelper(aUrls, getter_AddRefs(presentationUrls))))) {
667     return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
668   }
669 
670   nsCOMPtr<nsIArray> devices;
671   nsresult rv = deviceManager->GetAvailableDevices(presentationUrls,
672                                                    getter_AddRefs(devices));
673   if (NS_WARN_IF(NS_FAILED(rv))) {
674     return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
675   }
676 
677   nsCOMPtr<nsISimpleEnumerator> enumerator;
678   rv = devices->Enumerate(getter_AddRefs(enumerator));
679   if (NS_WARN_IF(NS_FAILED(rv))) {
680     return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
681   }
682 
683   NS_ConvertUTF16toUTF8 utf8DeviceId(aDeviceId);
684   bool hasMore;
685   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
686     nsCOMPtr<nsISupports> isupports;
687     rv = enumerator->GetNext(getter_AddRefs(isupports));
688 
689     nsCOMPtr<nsIPresentationDevice> device(do_QueryInterface(isupports));
690     MOZ_ASSERT(device);
691 
692     nsAutoCString id;
693     if (NS_SUCCEEDED(device->GetId(id)) && id.Equals(utf8DeviceId)) {
694       request->Select(device);
695       return NS_OK;
696     }
697   }
698 
699   // Reject if designated device is not available.
700   return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
701 }
702 
703 already_AddRefed<PresentationSessionInfo>
CreateControllingSessionInfo(const nsAString & aUrl,const nsAString & aSessionId,uint64_t aWindowId)704 PresentationService::CreateControllingSessionInfo(const nsAString& aUrl,
705                                                   const nsAString& aSessionId,
706                                                   uint64_t aWindowId) {
707   MOZ_ASSERT(NS_IsMainThread());
708 
709   if (aSessionId.IsEmpty()) {
710     return nullptr;
711   }
712 
713   RefPtr<PresentationSessionInfo> info =
714       new PresentationControllingInfo(aUrl, aSessionId);
715 
716   mSessionInfoAtController.Put(aSessionId, info);
717   AddRespondingSessionId(aWindowId, aSessionId,
718                          nsIPresentationService::ROLE_CONTROLLER);
719   return info.forget();
720 }
721 
722 NS_IMETHODIMP
SendSessionMessage(const nsAString & aSessionId,uint8_t aRole,const nsAString & aData)723 PresentationService::SendSessionMessage(const nsAString& aSessionId,
724                                         uint8_t aRole, const nsAString& aData) {
725   MOZ_ASSERT(NS_IsMainThread());
726   MOZ_ASSERT(!aData.IsEmpty());
727   MOZ_ASSERT(!aSessionId.IsEmpty());
728   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
729              aRole == nsIPresentationService::ROLE_RECEIVER);
730 
731   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
732   if (NS_WARN_IF(!info)) {
733     return NS_ERROR_NOT_AVAILABLE;
734   }
735 
736   return info->Send(aData);
737 }
738 
739 NS_IMETHODIMP
SendSessionBinaryMsg(const nsAString & aSessionId,uint8_t aRole,const nsACString & aData)740 PresentationService::SendSessionBinaryMsg(const nsAString& aSessionId,
741                                           uint8_t aRole,
742                                           const nsACString& aData) {
743   MOZ_ASSERT(NS_IsMainThread());
744   MOZ_ASSERT(!aData.IsEmpty());
745   MOZ_ASSERT(!aSessionId.IsEmpty());
746   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
747              aRole == nsIPresentationService::ROLE_RECEIVER);
748 
749   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
750   if (NS_WARN_IF(!info)) {
751     return NS_ERROR_NOT_AVAILABLE;
752   }
753 
754   return info->SendBinaryMsg(aData);
755 }
756 
757 NS_IMETHODIMP
SendSessionBlob(const nsAString & aSessionId,uint8_t aRole,nsIDOMBlob * aBlob)758 PresentationService::SendSessionBlob(const nsAString& aSessionId, uint8_t aRole,
759                                      nsIDOMBlob* aBlob) {
760   MOZ_ASSERT(NS_IsMainThread());
761   MOZ_ASSERT(!aSessionId.IsEmpty());
762   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
763              aRole == nsIPresentationService::ROLE_RECEIVER);
764   MOZ_ASSERT(aBlob);
765 
766   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
767   if (NS_WARN_IF(!info)) {
768     return NS_ERROR_NOT_AVAILABLE;
769   }
770 
771   return info->SendBlob(aBlob);
772 }
773 
774 NS_IMETHODIMP
CloseSession(const nsAString & aSessionId,uint8_t aRole,uint8_t aClosedReason)775 PresentationService::CloseSession(const nsAString& aSessionId, uint8_t aRole,
776                                   uint8_t aClosedReason) {
777   PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__,
778              NS_ConvertUTF16toUTF8(aSessionId).get(), aClosedReason, aRole);
779 
780   MOZ_ASSERT(NS_IsMainThread());
781   MOZ_ASSERT(!aSessionId.IsEmpty());
782   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
783              aRole == nsIPresentationService::ROLE_RECEIVER);
784 
785   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
786   if (NS_WARN_IF(!info)) {
787     return NS_ERROR_NOT_AVAILABLE;
788   }
789 
790   if (aClosedReason == nsIPresentationService::CLOSED_REASON_WENTAWAY) {
791     // Remove nsIPresentationSessionListener since we don't want to dispatch
792     // PresentationConnectionCloseEvent if the page is went away.
793     info->SetListener(nullptr);
794   }
795 
796   return info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED);
797 }
798 
799 NS_IMETHODIMP
TerminateSession(const nsAString & aSessionId,uint8_t aRole)800 PresentationService::TerminateSession(const nsAString& aSessionId,
801                                       uint8_t aRole) {
802   PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
803              NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
804 
805   MOZ_ASSERT(NS_IsMainThread());
806   MOZ_ASSERT(!aSessionId.IsEmpty());
807   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
808              aRole == nsIPresentationService::ROLE_RECEIVER);
809 
810   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
811   if (NS_WARN_IF(!info)) {
812     return NS_ERROR_NOT_AVAILABLE;
813   }
814 
815   return info->Close(NS_OK, nsIPresentationSessionListener::STATE_TERMINATED);
816 }
817 
818 NS_IMETHODIMP
ReconnectSession(const nsTArray<nsString> & aUrls,const nsAString & aSessionId,uint8_t aRole,nsIPresentationServiceCallback * aCallback)819 PresentationService::ReconnectSession(
820     const nsTArray<nsString>& aUrls, const nsAString& aSessionId, uint8_t aRole,
821     nsIPresentationServiceCallback* aCallback) {
822   PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get());
823 
824   MOZ_ASSERT(NS_IsMainThread());
825   MOZ_ASSERT(!aSessionId.IsEmpty());
826   MOZ_ASSERT(aCallback);
827   MOZ_ASSERT(!aUrls.IsEmpty());
828 
829   if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
830     MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
831     return NS_ERROR_INVALID_ARG;
832   }
833 
834   if (NS_WARN_IF(!aCallback)) {
835     return NS_ERROR_INVALID_ARG;
836   }
837 
838   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
839   if (NS_WARN_IF(!info)) {
840     return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
841   }
842 
843   if (NS_WARN_IF(!aUrls.Contains(info->GetUrl()))) {
844     return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
845   }
846 
847   return static_cast<PresentationControllingInfo*>(info.get())
848       ->Reconnect(aCallback);
849 }
850 
851 NS_IMETHODIMP
BuildTransport(const nsAString & aSessionId,uint8_t aRole)852 PresentationService::BuildTransport(const nsAString& aSessionId,
853                                     uint8_t aRole) {
854   MOZ_ASSERT(NS_IsMainThread());
855   MOZ_ASSERT(!aSessionId.IsEmpty());
856 
857   if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
858     MOZ_ASSERT(false, "Only controller can call BuildTransport.");
859     return NS_ERROR_INVALID_ARG;
860   }
861 
862   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
863   if (NS_WARN_IF(!info)) {
864     return NS_ERROR_NOT_AVAILABLE;
865   }
866 
867   return static_cast<PresentationControllingInfo*>(info.get())
868       ->BuildTransport();
869 }
870 
871 NS_IMETHODIMP
RegisterAvailabilityListener(const nsTArray<nsString> & aAvailabilityUrls,nsIPresentationAvailabilityListener * aListener)872 PresentationService::RegisterAvailabilityListener(
873     const nsTArray<nsString>& aAvailabilityUrls,
874     nsIPresentationAvailabilityListener* aListener) {
875   MOZ_ASSERT(NS_IsMainThread());
876   MOZ_ASSERT(!aAvailabilityUrls.IsEmpty());
877   MOZ_ASSERT(aListener);
878 
879   mAvailabilityManager.AddAvailabilityListener(aAvailabilityUrls, aListener);
880   return UpdateAvailabilityUrlChange(aAvailabilityUrls);
881 }
882 
883 NS_IMETHODIMP
UnregisterAvailabilityListener(const nsTArray<nsString> & aAvailabilityUrls,nsIPresentationAvailabilityListener * aListener)884 PresentationService::UnregisterAvailabilityListener(
885     const nsTArray<nsString>& aAvailabilityUrls,
886     nsIPresentationAvailabilityListener* aListener) {
887   MOZ_ASSERT(NS_IsMainThread());
888 
889   mAvailabilityManager.RemoveAvailabilityListener(aAvailabilityUrls, aListener);
890   return NS_OK;
891 }
892 
893 NS_IMETHODIMP
RegisterSessionListener(const nsAString & aSessionId,uint8_t aRole,nsIPresentationSessionListener * aListener)894 PresentationService::RegisterSessionListener(
895     const nsAString& aSessionId, uint8_t aRole,
896     nsIPresentationSessionListener* aListener) {
897   PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
898              NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
899 
900   MOZ_ASSERT(NS_IsMainThread());
901   MOZ_ASSERT(aListener);
902   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
903              aRole == nsIPresentationService::ROLE_RECEIVER);
904 
905   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
906   if (NS_WARN_IF(!info)) {
907     // Notify the listener of TERMINATED since no correspondent session info is
908     // available possibly due to establishment failure. This would be useful at
909     // the receiver side, since a presentation session is created at beginning
910     // and here is the place to realize the underlying establishment fails.
911     nsresult rv = aListener->NotifyStateChange(
912         aSessionId, nsIPresentationSessionListener::STATE_TERMINATED,
913         NS_ERROR_NOT_AVAILABLE);
914     if (NS_WARN_IF(NS_FAILED(rv))) {
915       return rv;
916     }
917     return NS_ERROR_NOT_AVAILABLE;
918   }
919 
920   return info->SetListener(aListener);
921 }
922 
923 NS_IMETHODIMP
UnregisterSessionListener(const nsAString & aSessionId,uint8_t aRole)924 PresentationService::UnregisterSessionListener(const nsAString& aSessionId,
925                                                uint8_t aRole) {
926   PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
927              NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
928 
929   MOZ_ASSERT(NS_IsMainThread());
930   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
931              aRole == nsIPresentationService::ROLE_RECEIVER);
932 
933   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
934   if (info) {
935     // When content side decide not handling this session anymore, simply
936     // close the connection. Session info is kept for reconnection.
937     Unused << NS_WARN_IF(NS_FAILED(
938         info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED)));
939     return info->SetListener(nullptr);
940   }
941   return NS_OK;
942 }
943 
944 NS_IMETHODIMP
RegisterRespondingListener(uint64_t aWindowId,nsIPresentationRespondingListener * aListener)945 PresentationService::RegisterRespondingListener(
946     uint64_t aWindowId, nsIPresentationRespondingListener* aListener) {
947   PRES_DEBUG("%s:windowId[%" PRIu64 "]\n", __func__, aWindowId);
948 
949   MOZ_ASSERT(NS_IsMainThread());
950   MOZ_ASSERT(aListener);
951 
952   nsCOMPtr<nsIPresentationRespondingListener> listener;
953   if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) {
954     return (listener == aListener) ? NS_OK : NS_ERROR_DOM_INVALID_STATE_ERR;
955   }
956 
957   nsTArray<nsString> sessionIdArray;
958   nsresult rv =
959       mReceiverSessionIdManager.GetSessionIds(aWindowId, sessionIdArray);
960   if (NS_WARN_IF(NS_FAILED(rv))) {
961     return rv;
962   }
963 
964   for (const auto& id : sessionIdArray) {
965     aListener->NotifySessionConnect(aWindowId, id);
966   }
967 
968   mRespondingListeners.Put(aWindowId, aListener);
969   return NS_OK;
970 }
971 
972 NS_IMETHODIMP
UnregisterRespondingListener(uint64_t aWindowId)973 PresentationService::UnregisterRespondingListener(uint64_t aWindowId) {
974   PRES_DEBUG("%s:windowId[%" PRIu64 "]\n", __func__, aWindowId);
975 
976   MOZ_ASSERT(NS_IsMainThread());
977 
978   mRespondingListeners.Remove(aWindowId);
979   return NS_OK;
980 }
981 
982 NS_IMETHODIMP
NotifyReceiverReady(const nsAString & aSessionId,uint64_t aWindowId,bool aIsLoading,nsIPresentationTransportBuilderConstructor * aBuilderConstructor)983 PresentationService::NotifyReceiverReady(
984     const nsAString& aSessionId, uint64_t aWindowId, bool aIsLoading,
985     nsIPresentationTransportBuilderConstructor* aBuilderConstructor) {
986   PRES_DEBUG("%s:id[%s], windowId[%" PRIu64 "], loading[%d]\n", __func__,
987              NS_ConvertUTF16toUTF8(aSessionId).get(), aWindowId, aIsLoading);
988 
989   RefPtr<PresentationSessionInfo> info =
990       GetSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER);
991   if (NS_WARN_IF(!info)) {
992     return NS_ERROR_NOT_AVAILABLE;
993   }
994 
995   AddRespondingSessionId(aWindowId, aSessionId,
996                          nsIPresentationService::ROLE_RECEIVER);
997 
998   if (!aIsLoading) {
999     return static_cast<PresentationPresentingInfo*>(info.get())
1000         ->NotifyResponderFailure();
1001   }
1002 
1003   nsCOMPtr<nsIPresentationRespondingListener> listener;
1004   if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) {
1005     nsresult rv = listener->NotifySessionConnect(aWindowId, aSessionId);
1006     if (NS_WARN_IF(NS_FAILED(rv))) {
1007       return rv;
1008     }
1009   }
1010 
1011   info->SetTransportBuilderConstructor(aBuilderConstructor);
1012   return static_cast<PresentationPresentingInfo*>(info.get())
1013       ->NotifyResponderReady();
1014 }
1015 
NotifyTransportClosed(const nsAString & aSessionId,uint8_t aRole,nsresult aReason)1016 nsresult PresentationService::NotifyTransportClosed(const nsAString& aSessionId,
1017                                                     uint8_t aRole,
1018                                                     nsresult aReason) {
1019   PRES_DEBUG("%s:id[%s], reason[%" PRIx32 "], role[%d]\n", __func__,
1020              NS_ConvertUTF16toUTF8(aSessionId).get(),
1021              static_cast<uint32_t>(aReason), aRole);
1022 
1023   MOZ_ASSERT(NS_IsMainThread());
1024   MOZ_ASSERT(!aSessionId.IsEmpty());
1025   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
1026              aRole == nsIPresentationService::ROLE_RECEIVER);
1027 
1028   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
1029   if (NS_WARN_IF(!info)) {
1030     return NS_ERROR_NOT_AVAILABLE;
1031   }
1032 
1033   return info->NotifyTransportClosed(aReason);
1034 }
1035 
1036 NS_IMETHODIMP
UntrackSessionInfo(const nsAString & aSessionId,uint8_t aRole)1037 PresentationService::UntrackSessionInfo(const nsAString& aSessionId,
1038                                         uint8_t aRole) {
1039   PRES_DEBUG("%s:id[%s], role[%d]\n", __func__,
1040              NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
1041 
1042   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
1043              aRole == nsIPresentationService::ROLE_RECEIVER);
1044   // Remove the session info.
1045   if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
1046     mSessionInfoAtController.Remove(aSessionId);
1047   } else {
1048     // Terminate receiver page.
1049     uint64_t windowId;
1050     nsresult rv = GetWindowIdBySessionIdInternal(aSessionId, aRole, &windowId);
1051     if (NS_SUCCEEDED(rv)) {
1052       NS_DispatchToMainThread(NS_NewRunnableFunction(
1053           "dom::PresentationService::UntrackSessionInfo", [windowId]() -> void {
1054             PRES_DEBUG("Attempt to close window[%" PRIu64 "]\n", windowId);
1055 
1056             if (auto* window =
1057                     nsGlobalWindowInner::GetInnerWindowWithId(windowId)) {
1058               window->Close();
1059             }
1060           }));
1061     }
1062 
1063     mSessionInfoAtReceiver.Remove(aSessionId);
1064   }
1065 
1066   // Remove the in-process responding info if there's still any.
1067   RemoveRespondingSessionId(aSessionId, aRole);
1068 
1069   return NS_OK;
1070 }
1071 
1072 NS_IMETHODIMP
GetWindowIdBySessionId(const nsAString & aSessionId,uint8_t aRole,uint64_t * aWindowId)1073 PresentationService::GetWindowIdBySessionId(const nsAString& aSessionId,
1074                                             uint8_t aRole,
1075                                             uint64_t* aWindowId) {
1076   return GetWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId);
1077 }
1078 
1079 NS_IMETHODIMP
UpdateWindowIdBySessionId(const nsAString & aSessionId,uint8_t aRole,const uint64_t aWindowId)1080 PresentationService::UpdateWindowIdBySessionId(const nsAString& aSessionId,
1081                                                uint8_t aRole,
1082                                                const uint64_t aWindowId) {
1083   return UpdateWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId);
1084 }
1085 
IsSessionAccessible(const nsAString & aSessionId,const uint8_t aRole,base::ProcessId aProcessId)1086 bool PresentationService::IsSessionAccessible(const nsAString& aSessionId,
1087                                               const uint8_t aRole,
1088                                               base::ProcessId aProcessId) {
1089   MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
1090              aRole == nsIPresentationService::ROLE_RECEIVER);
1091   RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
1092   if (NS_WARN_IF(!info)) {
1093     return false;
1094   }
1095   return info->IsAccessible(aProcessId);
1096 }
1097 
1098 }  // namespace dom
1099 }  // namespace mozilla
1100 
NS_CreatePresentationService()1101 already_AddRefed<nsIPresentationService> NS_CreatePresentationService() {
1102   MOZ_ASSERT(NS_IsMainThread());
1103 
1104   nsCOMPtr<nsIPresentationService> service;
1105   if (XRE_GetProcessType() == GeckoProcessType_Content) {
1106     service = new mozilla::dom::PresentationIPCService();
1107   } else {
1108     service = new PresentationService();
1109     if (NS_WARN_IF(!static_cast<PresentationService*>(service.get())->Init())) {
1110       return nullptr;
1111     }
1112   }
1113 
1114   return service.forget();
1115 }
1116