Coverage Report

Created: 2018-09-25 14:53

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