Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/presentation/PresentationConnection.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 "PresentationConnection.h"
8
9
#include "ControllerConnectionCollection.h"
10
#include "mozilla/AsyncEventDispatcher.h"
11
#include "mozilla/dom/DOMException.h"
12
#include "mozilla/dom/File.h"
13
#include "mozilla/dom/MessageEvent.h"
14
#include "mozilla/dom/MessageEventBinding.h"
15
#include "mozilla/dom/PresentationConnectionCloseEvent.h"
16
#include "mozilla/ErrorNames.h"
17
#include "mozilla/DebugOnly.h"
18
#include "mozilla/IntegerPrintfMacros.h"
19
#include "nsContentUtils.h"
20
#include "nsCycleCollectionParticipant.h"
21
#include "nsIPresentationService.h"
22
#include "nsServiceManagerUtils.h"
23
#include "nsStringStream.h"
24
#include "PresentationConnectionList.h"
25
#include "PresentationLog.h"
26
27
using namespace mozilla;
28
using namespace mozilla::dom;
29
30
NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationConnection)
31
32
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
33
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningConnectionList)
34
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
35
36
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
37
0
  tmp->Shutdown();
38
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningConnectionList)
39
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
40
41
NS_IMPL_ADDREF_INHERITED(PresentationConnection, DOMEventTargetHelper)
42
NS_IMPL_RELEASE_INHERITED(PresentationConnection, DOMEventTargetHelper)
43
44
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PresentationConnection)
45
0
  NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener)
46
0
  NS_INTERFACE_MAP_ENTRY(nsIRequest)
47
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
48
49
PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
50
                                               const nsAString& aId,
51
                                               const nsAString& aUrl,
52
                                               const uint8_t aRole,
53
                                               PresentationConnectionList* aList)
54
  : DOMEventTargetHelper(aWindow)
55
  , mId(aId)
56
  , mUrl(aUrl)
57
  , mState(PresentationConnectionState::Connecting)
58
  , mOwningConnectionList(aList)
59
  , mBinaryType(PresentationConnectionBinaryType::Arraybuffer)
60
0
{
61
0
  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
62
0
             aRole == nsIPresentationService::ROLE_RECEIVER);
63
0
  mRole = aRole;
64
0
}
65
66
/* virtual */ PresentationConnection::~PresentationConnection()
67
0
{
68
0
}
69
70
/* static */ already_AddRefed<PresentationConnection>
71
PresentationConnection::Create(nsPIDOMWindowInner* aWindow,
72
                               const nsAString& aId,
73
                               const nsAString& aUrl,
74
                               const uint8_t aRole,
75
                               PresentationConnectionList* aList)
76
0
{
77
0
  MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
78
0
             aRole == nsIPresentationService::ROLE_RECEIVER);
79
0
  RefPtr<PresentationConnection> connection =
80
0
    new PresentationConnection(aWindow, aId, aUrl, aRole, aList);
81
0
  if (NS_WARN_IF(!connection->Init())) {
82
0
    return nullptr;
83
0
  }
84
0
85
0
  if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
86
0
    ControllerConnectionCollection::GetSingleton()->AddConnection(connection,
87
0
                                                                  aRole);
88
0
  }
89
0
90
0
  return connection.forget();
91
0
}
92
93
bool
94
PresentationConnection::Init()
95
0
{
96
0
  if (NS_WARN_IF(mId.IsEmpty())) {
97
0
    return false;
98
0
  }
99
0
100
0
  nsCOMPtr<nsIPresentationService> service =
101
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
102
0
  if(NS_WARN_IF(!service)) {
103
0
    return false;
104
0
  }
105
0
106
0
  nsresult rv = service->RegisterSessionListener(mId, mRole, this);
107
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
108
0
    return false;
109
0
  }
110
0
111
0
  rv = AddIntoLoadGroup();
112
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
113
0
    return false;
114
0
  }
115
0
116
0
  return true;
117
0
}
118
119
void
120
PresentationConnection::Shutdown()
121
0
{
122
0
  PRES_DEBUG("connection shutdown:id[%s], role[%d]\n",
123
0
             NS_ConvertUTF16toUTF8(mId).get(), mRole);
124
0
125
0
  nsCOMPtr<nsIPresentationService> service =
126
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
127
0
  if (NS_WARN_IF(!service)) {
128
0
    return;
129
0
  }
130
0
131
0
  DebugOnly<nsresult> rv = service->UnregisterSessionListener(mId, mRole);
132
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "UnregisterSessionListener failed");
133
0
134
0
  DebugOnly<nsresult> rv2 = RemoveFromLoadGroup();
135
0
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv2), "RemoveFromLoadGroup failed");
136
0
137
0
  if (mRole == nsIPresentationService::ROLE_CONTROLLER) {
138
0
    ControllerConnectionCollection::GetSingleton()->RemoveConnection(this,
139
0
                                                                     mRole);
140
0
  }
141
0
}
142
143
/* virtual */ void
144
PresentationConnection::DisconnectFromOwner()
145
0
{
146
0
  Unused << NS_WARN_IF(NS_FAILED(ProcessConnectionWentAway()));
147
0
  DOMEventTargetHelper::DisconnectFromOwner();
148
0
}
149
150
/* virtual */ JSObject*
151
PresentationConnection::WrapObject(JSContext* aCx,
152
                                   JS::Handle<JSObject*> aGivenProto)
153
0
{
154
0
  return PresentationConnection_Binding::Wrap(aCx, this, aGivenProto);
155
0
}
156
157
void
158
PresentationConnection::GetId(nsAString& aId) const
159
0
{
160
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
161
0
    aId = EmptyString();
162
0
    return;
163
0
  }
164
0
165
0
  aId = mId;
166
0
}
167
168
void
169
PresentationConnection::GetUrl(nsAString& aUrl) const
170
0
{
171
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
172
0
    aUrl = EmptyString();
173
0
    return;
174
0
  }
175
0
176
0
  aUrl = mUrl;
177
0
}
178
179
PresentationConnectionState
180
PresentationConnection::State() const
181
0
{
182
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
183
0
    return PresentationConnectionState::Terminated;
184
0
  }
185
0
186
0
  return mState;
187
0
}
188
189
PresentationConnectionBinaryType
190
PresentationConnection::BinaryType() const
191
0
{
192
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
193
0
    return PresentationConnectionBinaryType::Blob;
194
0
  }
195
0
196
0
  return mBinaryType;
197
0
}
198
199
void
200
PresentationConnection::SetBinaryType(PresentationConnectionBinaryType aType)
201
0
{
202
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
203
0
    return;
204
0
  }
205
0
206
0
  mBinaryType = aType;
207
0
}
208
209
void
210
PresentationConnection::Send(const nsAString& aData,
211
                             ErrorResult& aRv)
212
0
{
213
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
214
0
    return;
215
0
  }
216
0
217
0
  // Sending is not allowed if the session is not connected.
218
0
  if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
219
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
220
0
    return;
221
0
  }
222
0
223
0
  nsCOMPtr<nsIPresentationService> service =
224
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
225
0
  if(NS_WARN_IF(!service)) {
226
0
    AsyncCloseConnectionWithErrorMsg(
227
0
      NS_LITERAL_STRING("Unable to send message due to an internal error."));
228
0
    return;
229
0
  }
230
0
231
0
  nsresult rv = service->SendSessionMessage(mId, mRole, aData);
232
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
233
0
    const uint32_t kMaxMessageLength = 256;
234
0
    nsAutoString data(Substring(aData, 0, kMaxMessageLength));
235
0
236
0
    AsyncCloseConnectionWithErrorMsg(
237
0
      NS_LITERAL_STRING("Unable to send message: \"") + data +
238
0
      NS_LITERAL_STRING("\""));
239
0
  }
240
0
}
241
242
void
243
PresentationConnection::Send(Blob& aData,
244
                             ErrorResult& aRv)
245
0
{
246
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
247
0
    return;
248
0
  }
249
0
250
0
  if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
251
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
252
0
    return;
253
0
  }
254
0
255
0
  nsCOMPtr<nsIPresentationService> service =
256
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
257
0
  if(NS_WARN_IF(!service)) {
258
0
    AsyncCloseConnectionWithErrorMsg(
259
0
      NS_LITERAL_STRING("Unable to send message due to an internal error."));
260
0
    return;
261
0
  }
262
0
263
0
  nsresult rv = service->SendSessionBlob(mId, mRole, &aData);
264
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
265
0
    AsyncCloseConnectionWithErrorMsg(
266
0
      NS_LITERAL_STRING("Unable to send binary message for Blob message."));
267
0
  }
268
0
}
269
270
void
271
PresentationConnection::Send(const ArrayBuffer& aData,
272
                             ErrorResult& aRv)
273
0
{
274
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
275
0
    return;
276
0
  }
277
0
278
0
  if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
279
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
280
0
    return;
281
0
  }
282
0
283
0
  nsCOMPtr<nsIPresentationService> service =
284
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
285
0
  if(NS_WARN_IF(!service)) {
286
0
    AsyncCloseConnectionWithErrorMsg(
287
0
      NS_LITERAL_STRING("Unable to send message due to an internal error."));
288
0
    return;
289
0
  }
290
0
291
0
  aData.ComputeLengthAndData();
292
0
293
0
  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
294
0
295
0
  uint32_t length = aData.Length();
296
0
  char* data = reinterpret_cast<char*>(aData.Data());
297
0
  nsDependentCSubstring msgString(data, length);
298
0
299
0
  nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString);
300
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
301
0
    AsyncCloseConnectionWithErrorMsg(
302
0
      NS_LITERAL_STRING("Unable to send binary message for ArrayBuffer message."));
303
0
  }
304
0
}
305
306
void
307
PresentationConnection::Send(const ArrayBufferView& aData,
308
                             ErrorResult& aRv)
309
0
{
310
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
311
0
    return;
312
0
  }
313
0
314
0
  if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
315
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
316
0
    return;
317
0
  }
318
0
319
0
  nsCOMPtr<nsIPresentationService> service =
320
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
321
0
  if(NS_WARN_IF(!service)) {
322
0
    AsyncCloseConnectionWithErrorMsg(
323
0
      NS_LITERAL_STRING("Unable to send message due to an internal error."));
324
0
    return;
325
0
  }
326
0
327
0
  aData.ComputeLengthAndData();
328
0
329
0
  static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
330
0
331
0
  uint32_t length = aData.Length();
332
0
  char* data = reinterpret_cast<char*>(aData.Data());
333
0
  nsDependentCSubstring msgString(data, length);
334
0
335
0
  nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString);
336
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
337
0
    AsyncCloseConnectionWithErrorMsg(
338
0
      NS_LITERAL_STRING("Unable to send binary message for ArrayBufferView message."));
339
0
  }
340
0
}
341
342
void
343
PresentationConnection::Close(ErrorResult& aRv)
344
0
{
345
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
346
0
    return;
347
0
  }
348
0
349
0
  // It only works when the state is CONNECTED or CONNECTING.
350
0
  if (NS_WARN_IF(mState != PresentationConnectionState::Connected &&
351
0
                 mState != PresentationConnectionState::Connecting)) {
352
0
    return;
353
0
  }
354
0
355
0
  nsCOMPtr<nsIPresentationService> service =
356
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
357
0
  if(NS_WARN_IF(!service)) {
358
0
    aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
359
0
    return;
360
0
  }
361
0
362
0
  Unused << NS_WARN_IF(NS_FAILED(
363
0
    service->CloseSession(mId,
364
0
                          mRole,
365
0
                          nsIPresentationService::CLOSED_REASON_CLOSED)));
366
0
}
367
368
void
369
PresentationConnection::Terminate(ErrorResult& aRv)
370
0
{
371
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
372
0
    return;
373
0
  }
374
0
375
0
  // It only works when the state is CONNECTED.
376
0
  if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
377
0
    return;
378
0
  }
379
0
380
0
  nsCOMPtr<nsIPresentationService> service =
381
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
382
0
  if(NS_WARN_IF(!service)) {
383
0
    aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
384
0
    return;
385
0
  }
386
0
387
0
  Unused << NS_WARN_IF(NS_FAILED(service->TerminateSession(mId, mRole)));
388
0
}
389
390
bool
391
PresentationConnection::Equals(uint64_t aWindowId,
392
                               const nsAString& aId)
393
0
{
394
0
  return GetOwner() &&
395
0
         aWindowId == GetOwner()->WindowID() &&
396
0
         mId.Equals(aId);
397
0
}
398
399
NS_IMETHODIMP
400
PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
401
                                          uint16_t aState,
402
                                          nsresult aReason)
403
0
{
404
0
  PRES_DEBUG("connection state change:id[%s], state[%" PRIx32
405
0
             "], reason[%" PRIx32 "], role[%d]\n",
406
0
             NS_ConvertUTF16toUTF8(aSessionId).get(), aState,
407
0
             static_cast<uint32_t>(aReason), mRole);
408
0
409
0
  if (!aSessionId.Equals(mId)) {
410
0
    return NS_ERROR_INVALID_ARG;
411
0
  }
412
0
413
0
  // A terminated connection should always remain in terminated.
414
0
  if (mState == PresentationConnectionState::Terminated) {
415
0
    return NS_OK;
416
0
  }
417
0
418
0
  PresentationConnectionState state;
419
0
  switch (aState) {
420
0
    case nsIPresentationSessionListener::STATE_CONNECTING:
421
0
      state = PresentationConnectionState::Connecting;
422
0
      break;
423
0
    case nsIPresentationSessionListener::STATE_CONNECTED:
424
0
      state = PresentationConnectionState::Connected;
425
0
      break;
426
0
    case nsIPresentationSessionListener::STATE_CLOSED:
427
0
      state = PresentationConnectionState::Closed;
428
0
      break;
429
0
    case nsIPresentationSessionListener::STATE_TERMINATED:
430
0
      state = PresentationConnectionState::Terminated;
431
0
      break;
432
0
    default:
433
0
      NS_WARNING("Unknown presentation session state.");
434
0
      return NS_ERROR_INVALID_ARG;
435
0
  }
436
0
437
0
  if (mState == state) {
438
0
    return NS_OK;
439
0
  }
440
0
  mState = state;
441
0
442
0
  nsresult rv = ProcessStateChanged(aReason);
443
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
444
0
      return rv;
445
0
  }
446
0
447
0
  if (mOwningConnectionList) {
448
0
    mOwningConnectionList->NotifyStateChange(aSessionId, this);
449
0
  }
450
0
451
0
  return NS_OK;
452
0
}
453
454
nsresult
455
PresentationConnection::ProcessStateChanged(nsresult aReason)
456
0
{
457
0
  switch (mState) {
458
0
    case PresentationConnectionState::Connecting:
459
0
      return NS_OK;
460
0
    case PresentationConnectionState::Connected: {
461
0
      if (nsContentUtils::ShouldResistFingerprinting()) {
462
0
        return NS_OK;
463
0
      }
464
0
465
0
      RefPtr<AsyncEventDispatcher> asyncDispatcher =
466
0
        new AsyncEventDispatcher(this,
467
0
                                 NS_LITERAL_STRING("connect"),
468
0
                                 CanBubble::eNo);
469
0
      return asyncDispatcher->PostDOMEvent();
470
0
    }
471
0
    case PresentationConnectionState::Closed: {
472
0
      PresentationConnectionClosedReason reason =
473
0
        PresentationConnectionClosedReason::Closed;
474
0
475
0
      nsString errorMsg;
476
0
      if (NS_FAILED(aReason)) {
477
0
        reason = PresentationConnectionClosedReason::Error;
478
0
        nsCString name, message;
479
0
480
0
        // If aReason is not a DOM error, use error name as message.
481
0
        if (NS_FAILED(NS_GetNameAndMessageForDOMNSResult(aReason,
482
0
                                                         name,
483
0
                                                         message))) {
484
0
          mozilla::GetErrorName(aReason, message);
485
0
          message.InsertLiteral("Internal error: ", 0);
486
0
        }
487
0
        CopyUTF8toUTF16(message, errorMsg);
488
0
      }
489
0
490
0
      Unused <<
491
0
        NS_WARN_IF(NS_FAILED(DispatchConnectionCloseEvent(reason, errorMsg)));
492
0
493
0
      return RemoveFromLoadGroup();
494
0
    }
495
0
    case PresentationConnectionState::Terminated: {
496
0
      if (!nsContentUtils::ShouldResistFingerprinting()) {
497
0
        // Ensure onterminate event is fired.
498
0
        RefPtr<AsyncEventDispatcher> asyncDispatcher =
499
0
          new AsyncEventDispatcher(this,
500
0
                                   NS_LITERAL_STRING("terminate"),
501
0
                                   CanBubble::eNo);
502
0
        Unused << NS_WARN_IF(NS_FAILED(asyncDispatcher->PostDOMEvent()));
503
0
      }
504
0
505
0
      nsCOMPtr<nsIPresentationService> service =
506
0
        do_GetService(PRESENTATION_SERVICE_CONTRACTID);
507
0
      if (NS_WARN_IF(!service)) {
508
0
        return NS_ERROR_NOT_AVAILABLE;
509
0
      }
510
0
511
0
      nsresult rv = service->UnregisterSessionListener(mId, mRole);
512
0
      if(NS_WARN_IF(NS_FAILED(rv))) {
513
0
        return rv;
514
0
      }
515
0
516
0
      return RemoveFromLoadGroup();
517
0
    }
518
0
    default:
519
0
      MOZ_CRASH("Unknown presentation session state.");
520
0
      return NS_ERROR_INVALID_ARG;
521
0
  }
522
0
}
523
524
NS_IMETHODIMP
525
PresentationConnection::NotifyMessage(const nsAString& aSessionId,
526
                                      const nsACString& aData,
527
                                      bool aIsBinary)
528
0
{
529
0
  PRES_DEBUG("connection %s:id[%s], data[%s], role[%d]\n", __func__,
530
0
             NS_ConvertUTF16toUTF8(aSessionId).get(),
531
0
             nsPromiseFlatCString(aData).get(), mRole);
532
0
533
0
  if (!aSessionId.Equals(mId)) {
534
0
    return NS_ERROR_INVALID_ARG;
535
0
  }
536
0
537
0
  // No message should be expected when the session is not connected.
538
0
  if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
539
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
540
0
  }
541
0
542
0
  if (NS_WARN_IF(NS_FAILED(DoReceiveMessage(aData, aIsBinary)))) {
543
0
    AsyncCloseConnectionWithErrorMsg(
544
0
      NS_LITERAL_STRING("Unable to receive a message."));
545
0
    return NS_ERROR_FAILURE;
546
0
  }
547
0
548
0
  return NS_OK;
549
0
}
550
551
nsresult
552
PresentationConnection::DoReceiveMessage(const nsACString& aData, bool aIsBinary)
553
0
{
554
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
555
0
    return NS_OK;
556
0
  }
557
0
558
0
  // Transform the data.
559
0
  AutoJSAPI jsapi;
560
0
  if (!jsapi.Init(GetOwner())) {
561
0
    return NS_ERROR_FAILURE;
562
0
  }
563
0
  JSContext* cx = jsapi.cx();
564
0
  JS::Rooted<JS::Value> jsData(cx);
565
0
566
0
  nsresult rv;
567
0
  if (aIsBinary) {
568
0
    if (mBinaryType == PresentationConnectionBinaryType::Blob) {
569
0
      RefPtr<Blob> blob =
570
0
        Blob::CreateStringBlob(GetOwner(), aData, EmptyString());
571
0
      MOZ_ASSERT(blob);
572
0
573
0
      if (!ToJSValue(cx, blob, &jsData)) {
574
0
        return NS_ERROR_FAILURE;
575
0
      }
576
0
    } else if (mBinaryType == PresentationConnectionBinaryType::Arraybuffer) {
577
0
      JS::Rooted<JSObject*> arrayBuf(cx);
578
0
      rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
579
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
580
0
        return rv;
581
0
      }
582
0
      jsData.setObject(*arrayBuf);
583
0
    } else {
584
0
      MOZ_CRASH("Unknown binary type!");
585
0
    }
586
0
  } else {
587
0
    NS_ConvertUTF8toUTF16 utf16Data(aData);
588
0
    if(NS_WARN_IF(!ToJSValue(cx, utf16Data, &jsData))) {
589
0
      return NS_ERROR_FAILURE;
590
0
    }
591
0
  }
592
0
593
0
  return DispatchMessageEvent(jsData);
594
0
}
595
596
nsresult
597
PresentationConnection::DispatchConnectionCloseEvent(
598
  PresentationConnectionClosedReason aReason,
599
  const nsAString& aMessage,
600
  bool aDispatchNow)
601
0
{
602
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
603
0
    return NS_OK;
604
0
  }
605
0
606
0
  if (mState != PresentationConnectionState::Closed) {
607
0
    MOZ_ASSERT(false, "The connection state should be closed.");
608
0
    return NS_ERROR_FAILURE;
609
0
  }
610
0
611
0
  PresentationConnectionCloseEventInit init;
612
0
  init.mReason = aReason;
613
0
  init.mMessage = aMessage;
614
0
615
0
  RefPtr<PresentationConnectionCloseEvent> closedEvent =
616
0
    PresentationConnectionCloseEvent::Constructor(this,
617
0
                                                   NS_LITERAL_STRING("close"),
618
0
                                                   init);
619
0
  closedEvent->SetTrusted(true);
620
0
621
0
  if (aDispatchNow) {
622
0
    ErrorResult rv;
623
0
    DispatchEvent(*closedEvent, rv);
624
0
    return rv.StealNSResult();
625
0
  }
626
0
627
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
628
0
    new AsyncEventDispatcher(this, closedEvent);
629
0
  return asyncDispatcher->PostDOMEvent();
630
0
}
631
632
nsresult
633
PresentationConnection::DispatchMessageEvent(JS::Handle<JS::Value> aData)
634
0
{
635
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
636
0
  if (NS_WARN_IF(!global)) {
637
0
    return NS_ERROR_NOT_AVAILABLE;
638
0
  }
639
0
640
0
  // Get the origin.
641
0
  nsAutoString origin;
642
0
  nsresult rv = nsContentUtils::GetUTFOrigin(global->PrincipalOrNull(), origin);
643
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
644
0
    return rv;
645
0
  }
646
0
647
0
  RefPtr<MessageEvent> messageEvent = new MessageEvent(this, nullptr, nullptr);
648
0
649
0
  messageEvent->InitMessageEvent(nullptr,
650
0
                                 NS_LITERAL_STRING("message"),
651
0
                                 CanBubble::eNo, Cancelable::eNo, aData, origin,
652
0
                                 EmptyString(), nullptr,
653
0
                                 Sequence<OwningNonNull<MessagePort>>());
654
0
  messageEvent->SetTrusted(true);
655
0
656
0
  RefPtr<AsyncEventDispatcher> asyncDispatcher =
657
0
    new AsyncEventDispatcher(this, messageEvent);
658
0
  return asyncDispatcher->PostDOMEvent();
659
0
}
660
661
nsresult
662
PresentationConnection::ProcessConnectionWentAway()
663
0
{
664
0
  if (mState != PresentationConnectionState::Connected &&
665
0
      mState != PresentationConnectionState::Connecting) {
666
0
    // If the state is not connected or connecting, do not need to
667
0
    // close the session.
668
0
    return NS_OK;
669
0
  }
670
0
671
0
  mState = PresentationConnectionState::Terminated;
672
0
673
0
  nsCOMPtr<nsIPresentationService> service =
674
0
    do_GetService(PRESENTATION_SERVICE_CONTRACTID);
675
0
  if (NS_WARN_IF(!service)) {
676
0
    return NS_ERROR_NOT_AVAILABLE;
677
0
  }
678
0
679
0
  return service->CloseSession(
680
0
    mId, mRole, nsIPresentationService::CLOSED_REASON_WENTAWAY);
681
0
}
682
683
NS_IMETHODIMP
684
PresentationConnection::GetName(nsACString &aResult)
685
0
{
686
0
  aResult.AssignLiteral("about:presentation-connection");
687
0
  return NS_OK;
688
0
}
689
690
NS_IMETHODIMP
691
PresentationConnection::IsPending(bool* aRetval)
692
0
{
693
0
  *aRetval = true;
694
0
  return NS_OK;
695
0
}
696
697
NS_IMETHODIMP
698
PresentationConnection::GetStatus(nsresult* aStatus)
699
0
{
700
0
  *aStatus = NS_OK;
701
0
  return NS_OK;
702
0
}
703
704
NS_IMETHODIMP
705
PresentationConnection::Cancel(nsresult aStatus)
706
0
{
707
0
  nsCOMPtr<nsIRunnable> event =
708
0
    NewRunnableMethod("dom::PresentationConnection::ProcessConnectionWentAway",
709
0
                      this,
710
0
                      &PresentationConnection::ProcessConnectionWentAway);
711
0
  return NS_DispatchToCurrentThread(event);
712
0
}
713
NS_IMETHODIMP
714
PresentationConnection::Suspend(void)
715
0
{
716
0
  return NS_ERROR_NOT_IMPLEMENTED;
717
0
}
718
NS_IMETHODIMP
719
PresentationConnection::Resume(void)
720
0
{
721
0
  return NS_ERROR_NOT_IMPLEMENTED;
722
0
}
723
724
NS_IMETHODIMP
725
PresentationConnection::GetLoadGroup(nsILoadGroup** aLoadGroup)
726
0
{
727
0
  *aLoadGroup = nullptr;
728
0
729
0
  nsCOMPtr<nsIDocument> doc = GetOwner() ? GetOwner()->GetExtantDoc() : nullptr;
730
0
  if (!doc) {
731
0
    return NS_ERROR_FAILURE;
732
0
  }
733
0
734
0
  *aLoadGroup = doc->GetDocumentLoadGroup().take();
735
0
  return NS_OK;
736
0
}
737
738
NS_IMETHODIMP
739
PresentationConnection::SetLoadGroup(nsILoadGroup * aLoadGroup)
740
0
{
741
0
  return NS_ERROR_UNEXPECTED;
742
0
}
743
744
NS_IMETHODIMP
745
PresentationConnection::GetLoadFlags(nsLoadFlags* aLoadFlags)
746
0
{
747
0
  *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
748
0
  return NS_OK;
749
0
}
750
751
NS_IMETHODIMP
752
PresentationConnection::SetLoadFlags(nsLoadFlags aLoadFlags)
753
0
{
754
0
  return NS_OK;
755
0
}
756
757
nsresult
758
PresentationConnection::AddIntoLoadGroup()
759
0
{
760
0
  // Avoid adding to loadgroup multiple times
761
0
  if (mWeakLoadGroup) {
762
0
    return NS_OK;
763
0
  }
764
0
765
0
  nsCOMPtr<nsILoadGroup> loadGroup;
766
0
  nsresult rv = GetLoadGroup(getter_AddRefs(loadGroup));
767
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
768
0
    return rv;
769
0
  }
770
0
771
0
  rv = loadGroup->AddRequest(this, nullptr);
772
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
773
0
    return rv;
774
0
  }
775
0
776
0
  mWeakLoadGroup = do_GetWeakReference(loadGroup);
777
0
  return NS_OK;
778
0
}
779
780
nsresult
781
PresentationConnection::RemoveFromLoadGroup()
782
0
{
783
0
  if (!mWeakLoadGroup) {
784
0
    return NS_OK;
785
0
  }
786
0
787
0
  nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakLoadGroup);
788
0
  if (loadGroup) {
789
0
    mWeakLoadGroup = nullptr;
790
0
    return loadGroup->RemoveRequest(this, nullptr, NS_OK);
791
0
  }
792
0
793
0
  return NS_OK;
794
0
}
795
796
void
797
PresentationConnection::AsyncCloseConnectionWithErrorMsg(const nsAString& aMessage)
798
0
{
799
0
  if (mState == PresentationConnectionState::Terminated) {
800
0
    return;
801
0
  }
802
0
803
0
  nsString message = nsString(aMessage);
804
0
  RefPtr<PresentationConnection> self = this;
805
0
  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
806
0
    "dom::PresentationConnection::AsyncCloseConnectionWithErrorMsg",
807
0
    [self, message]() -> void {
808
0
      // Set |mState| to |PresentationConnectionState::Closed| here to avoid
809
0
      // calling |ProcessStateChanged|.
810
0
      self->mState = PresentationConnectionState::Closed;
811
0
812
0
      // Make sure dispatching the event and closing the connection are invoked
813
0
      // at the same time by setting |aDispatchNow| to true.
814
0
      Unused << NS_WARN_IF(NS_FAILED(
815
0
        self->DispatchConnectionCloseEvent(PresentationConnectionClosedReason::Error,
816
0
                                           message,
817
0
                                           true)));
818
0
819
0
      nsCOMPtr<nsIPresentationService> service =
820
0
        do_GetService(PRESENTATION_SERVICE_CONTRACTID);
821
0
      if(NS_WARN_IF(!service)) {
822
0
        return;
823
0
      }
824
0
825
0
      Unused << NS_WARN_IF(NS_FAILED(
826
0
        service->CloseSession(self->mId,
827
0
                              self->mRole,
828
0
                              nsIPresentationService::CLOSED_REASON_ERROR)));
829
0
    });
830
0
831
0
  Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)));
832
0
}