Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/messagechannel/MessagePort.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 "MessagePort.h"
8
9
#include "MessageEvent.h"
10
#include "MessagePortChild.h"
11
#include "mozilla/dom/BlobBinding.h"
12
#include "mozilla/dom/Event.h"
13
#include "mozilla/dom/File.h"
14
#include "mozilla/dom/MessageChannel.h"
15
#include "mozilla/dom/MessageEventBinding.h"
16
#include "mozilla/dom/MessagePortBinding.h"
17
#include "mozilla/dom/MessagePortChild.h"
18
#include "mozilla/dom/PMessagePort.h"
19
#include "mozilla/dom/ScriptSettings.h"
20
#include "mozilla/dom/StructuredCloneTags.h"
21
#include "mozilla/dom/WorkerRef.h"
22
#include "mozilla/dom/WorkerScope.h"
23
#include "mozilla/ipc/BackgroundChild.h"
24
#include "mozilla/ipc/PBackgroundChild.h"
25
#include "mozilla/MessagePortTimelineMarker.h"
26
#include "mozilla/ScopeExit.h"
27
#include "mozilla/TimelineConsumers.h"
28
#include "mozilla/TimelineMarker.h"
29
#include "mozilla/Unused.h"
30
#include "nsContentUtils.h"
31
#include "nsGlobalWindow.h"
32
#include "nsPresContext.h"
33
#include "SharedMessagePortMessage.h"
34
35
#include "nsIBFCacheEntry.h"
36
#include "nsIDocument.h"
37
#include "nsIPresShell.h"
38
#include "nsISupportsPrimitives.h"
39
#include "nsServiceManagerUtils.h"
40
41
#ifdef XP_WIN
42
#undef PostMessage
43
#endif
44
45
namespace mozilla {
46
namespace dom {
47
48
class PostMessageRunnable final : public CancelableRunnable
49
{
50
  friend class MessagePort;
51
52
public:
53
  PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
54
    : CancelableRunnable("dom::PostMessageRunnable")
55
    , mPort(aPort)
56
    , mData(aData)
57
0
  {
58
0
    MOZ_ASSERT(aPort);
59
0
    MOZ_ASSERT(aData);
60
0
  }
61
62
  NS_IMETHOD
63
  Run() override
64
0
  {
65
0
    NS_ASSERT_OWNINGTHREAD(Runnable);
66
0
67
0
    // The port can be cycle collected while this runnable is pending in
68
0
    // the event queue.
69
0
    if (!mPort) {
70
0
      return NS_OK;
71
0
    }
72
0
73
0
    MOZ_ASSERT(mPort->mPostMessageRunnable == this);
74
0
75
0
    nsresult rv = DispatchMessage();
76
0
77
0
    // We must check if we were waiting for this message in order to shutdown
78
0
    // the port.
79
0
    mPort->UpdateMustKeepAlive();
80
0
81
0
    mPort->mPostMessageRunnable = nullptr;
82
0
    mPort->Dispatch();
83
0
84
0
    return rv;
85
0
  }
86
87
  nsresult
88
  Cancel() override
89
0
  {
90
0
    NS_ASSERT_OWNINGTHREAD(Runnable);
91
0
92
0
    mPort = nullptr;
93
0
    mData = nullptr;
94
0
    return NS_OK;
95
0
  }
96
97
private:
98
  nsresult
99
  DispatchMessage() const
100
0
  {
101
0
    NS_ASSERT_OWNINGTHREAD(Runnable);
102
0
103
0
    nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
104
0
105
0
    AutoJSAPI jsapi;
106
0
    if (!globalObject || !jsapi.Init(globalObject)) {
107
0
      NS_WARNING("Failed to initialize AutoJSAPI object.");
108
0
      return NS_ERROR_FAILURE;
109
0
    }
110
0
111
0
    JSContext* cx = jsapi.cx();
112
0
113
0
    ErrorResult rv;
114
0
    JS::Rooted<JS::Value> value(cx);
115
0
116
0
    UniquePtr<AbstractTimelineMarker> start;
117
0
    UniquePtr<AbstractTimelineMarker> end;
118
0
    RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
119
0
    bool isTimelineRecording = timelines && !timelines->IsEmpty();
120
0
121
0
    if (isTimelineRecording) {
122
0
      start = MakeUnique<MessagePortTimelineMarker>(
123
0
        ProfileTimelineMessagePortOperationType::DeserializeData,
124
0
        MarkerTracingType::START);
125
0
    }
126
0
127
0
    mData->Read(cx, &value, rv);
128
0
129
0
    if (isTimelineRecording) {
130
0
      end = MakeUnique<MessagePortTimelineMarker>(
131
0
        ProfileTimelineMessagePortOperationType::DeserializeData,
132
0
        MarkerTracingType::END);
133
0
      timelines->AddMarkerForAllObservedDocShells(start);
134
0
      timelines->AddMarkerForAllObservedDocShells(end);
135
0
    }
136
0
137
0
    if (NS_WARN_IF(rv.Failed())) {
138
0
      mPort->DispatchError();
139
0
      return rv.StealNSResult();
140
0
    }
141
0
142
0
    // Create the event
143
0
    nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
144
0
      do_QueryInterface(mPort->GetOwner());
145
0
    RefPtr<MessageEvent> event =
146
0
      new MessageEvent(eventTarget, nullptr, nullptr);
147
0
148
0
    Sequence<OwningNonNull<MessagePort>> ports;
149
0
    if (!mData->TakeTransferredPortsAsSequence(ports)) {
150
0
      mPort->DispatchError();
151
0
      return NS_ERROR_OUT_OF_MEMORY;
152
0
    }
153
0
154
0
    event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"),
155
0
                            CanBubble::eNo, Cancelable::eNo, value, EmptyString(),
156
0
                            EmptyString(), nullptr, ports);
157
0
    event->SetTrusted(true);
158
0
159
0
    mPort->DispatchEvent(*event);
160
0
161
0
    return NS_OK;
162
0
  }
163
164
private:
165
  ~PostMessageRunnable()
166
0
  {}
167
168
  RefPtr<MessagePort> mPort;
169
  RefPtr<SharedMessagePortMessage> mData;
170
};
171
172
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
173
174
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
175
0
                                                DOMEventTargetHelper)
176
0
  if (tmp->mPostMessageRunnable) {
177
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
178
0
  }
179
0
180
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
181
0
182
0
  tmp->CloseForced();
183
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
184
185
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
186
0
                                                  DOMEventTargetHelper)
187
0
  if (tmp->mPostMessageRunnable) {
188
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
189
0
  }
190
0
191
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
192
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
193
194
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePort)
195
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
196
197
NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
198
NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
199
200
MessagePort::MessagePort(nsIGlobalObject* aGlobal, State aState)
201
  : DOMEventTargetHelper(aGlobal)
202
  , mState(aState)
203
  , mMessageQueueEnabled(false)
204
  , mIsKeptAlive(false)
205
  , mHasBeenTransferredOrClosed(false)
206
0
{
207
0
  MOZ_ASSERT(aGlobal);
208
0
209
0
  mIdentifier = new MessagePortIdentifier();
210
0
  mIdentifier->neutered() = true;
211
0
  mIdentifier->sequenceId() = 0;
212
0
}
213
214
MessagePort::~MessagePort()
215
0
{
216
0
  CloseForced();
217
0
  MOZ_ASSERT(!mWorkerRef);
218
0
}
219
220
/* static */ already_AddRefed<MessagePort>
221
MessagePort::Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
222
                    const nsID& aDestinationUUID, ErrorResult& aRv)
223
0
{
224
0
  MOZ_ASSERT(aGlobal);
225
0
226
0
  RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateUnshippedEntangled);
227
0
  mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
228
0
                 false /* Neutered */, aRv);
229
0
  return mp.forget();
230
0
}
231
232
/* static */ already_AddRefed<MessagePort>
233
MessagePort::Create(nsIGlobalObject* aGlobal,
234
                    const MessagePortIdentifier& aIdentifier,
235
                    ErrorResult& aRv)
236
0
{
237
0
  MOZ_ASSERT(aGlobal);
238
0
239
0
  RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateEntangling);
240
0
  mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
241
0
                 aIdentifier.sequenceId(), aIdentifier.neutered(), aRv);
242
0
  return mp.forget();
243
0
}
244
245
void
246
MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
247
0
{
248
0
  MOZ_DIAGNOSTIC_ASSERT(aEntangledPort);
249
0
  MOZ_DIAGNOSTIC_ASSERT(!mUnshippedEntangledPort);
250
0
251
0
  mUnshippedEntangledPort = aEntangledPort;
252
0
}
253
254
void
255
MessagePort::Initialize(const nsID& aUUID,
256
                        const nsID& aDestinationUUID,
257
                        uint32_t aSequenceID, bool aNeutered,
258
                        ErrorResult& aRv)
259
0
{
260
0
  MOZ_ASSERT(mIdentifier);
261
0
  mIdentifier->uuid() = aUUID;
262
0
  mIdentifier->destinationUuid() = aDestinationUUID;
263
0
  mIdentifier->sequenceId() = aSequenceID;
264
0
265
0
  if (aNeutered) {
266
0
    // If this port is neutered we don't want to keep it alive artificially nor
267
0
    // we want to add listeners or WorkerRefs.
268
0
    mState = eStateDisentangled;
269
0
    return;
270
0
  }
271
0
272
0
  if (mState == eStateEntangling) {
273
0
    if (!ConnectToPBackground()) {
274
0
      aRv.Throw(NS_ERROR_FAILURE);
275
0
      return;
276
0
    }
277
0
  } else {
278
0
    MOZ_ASSERT(mState == eStateUnshippedEntangled);
279
0
  }
280
0
281
0
  // The port has to keep itself alive until it's entangled.
282
0
  UpdateMustKeepAlive();
283
0
284
0
  if (!NS_IsMainThread()) {
285
0
    RefPtr<MessagePort> self = this;
286
0
287
0
    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
288
0
    MOZ_ASSERT(workerPrivate);
289
0
290
0
    // When the callback is executed, we cannot process messages anymore because
291
0
    // we cannot dispatch new runnables. Let's force a Close().
292
0
    RefPtr<StrongWorkerRef> strongWorkerRef =
293
0
      StrongWorkerRef::Create(workerPrivate, "MessagePort",
294
0
                              [self]() { self->CloseForced(); });
295
0
    if (NS_WARN_IF(!strongWorkerRef)) {
296
0
      // The worker is shutting down.
297
0
      mState = eStateDisentangled;
298
0
      UpdateMustKeepAlive();
299
0
      aRv.Throw(NS_ERROR_FAILURE);
300
0
      return;
301
0
    }
302
0
303
0
    MOZ_ASSERT(!mWorkerRef);
304
0
    mWorkerRef = std::move(strongWorkerRef);
305
0
  }
306
0
}
307
308
JSObject*
309
MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
310
0
{
311
0
  return MessagePort_Binding::Wrap(aCx, this, aGivenProto);
312
0
}
313
314
void
315
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
316
                         const Sequence<JSObject*>& aTransferable,
317
                         ErrorResult& aRv)
318
0
{
319
0
  // We *must* clone the data here, or the JS::Value could be modified
320
0
  // by script
321
0
322
0
  // Here we want to check if the transerable object list contains
323
0
  // this port.
324
0
  for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
325
0
    JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
326
0
    if (!object) {
327
0
      continue;
328
0
    }
329
0
330
0
    MessagePort* port = nullptr;
331
0
    nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
332
0
    if (NS_SUCCEEDED(rv) && port == this) {
333
0
      aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
334
0
      return;
335
0
    }
336
0
  }
337
0
338
0
  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
339
0
340
0
  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
341
0
                                                          &transferable);
342
0
  if (NS_WARN_IF(aRv.Failed())) {
343
0
    return;
344
0
  }
345
0
346
0
  RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
347
0
348
0
  UniquePtr<AbstractTimelineMarker> start;
349
0
  UniquePtr<AbstractTimelineMarker> end;
350
0
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
351
0
  bool isTimelineRecording = timelines && !timelines->IsEmpty();
352
0
353
0
  if (isTimelineRecording) {
354
0
    start = MakeUnique<MessagePortTimelineMarker>(
355
0
      ProfileTimelineMessagePortOperationType::SerializeData,
356
0
      MarkerTracingType::START);
357
0
  }
358
0
359
0
  data->Write(aCx, aMessage, transferable, aRv);
360
0
361
0
  if (isTimelineRecording) {
362
0
    end = MakeUnique<MessagePortTimelineMarker>(
363
0
      ProfileTimelineMessagePortOperationType::SerializeData,
364
0
      MarkerTracingType::END);
365
0
    timelines->AddMarkerForAllObservedDocShells(start);
366
0
    timelines->AddMarkerForAllObservedDocShells(end);
367
0
  }
368
0
369
0
  if (NS_WARN_IF(aRv.Failed())) {
370
0
    return;
371
0
  }
372
0
373
0
  // This message has to be ignored.
374
0
  if (mState > eStateEntangled) {
375
0
    return;
376
0
  }
377
0
378
0
  // If we are unshipped we are connected to the other port on the same thread.
379
0
  if (mState == eStateUnshippedEntangled) {
380
0
    MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
381
0
    mUnshippedEntangledPort->mMessages.AppendElement(data);
382
0
    mUnshippedEntangledPort->Dispatch();
383
0
    return;
384
0
  }
385
0
386
0
  // Not entangled yet, but already closed/disentangled.
387
0
  if (mState == eStateEntanglingForDisentangle ||
388
0
      mState == eStateEntanglingForClose) {
389
0
    return;
390
0
  }
391
0
392
0
  RemoveDocFromBFCache();
393
0
394
0
  // Not entangled yet.
395
0
  if (mState == eStateEntangling) {
396
0
    mMessagesForTheOtherPort.AppendElement(data);
397
0
    return;
398
0
  }
399
0
400
0
  MOZ_ASSERT(mActor);
401
0
  MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
402
0
403
0
  AutoTArray<RefPtr<SharedMessagePortMessage>, 1> array;
404
0
  array.AppendElement(data);
405
0
406
0
  AutoTArray<ClonedMessageData, 1> messages;
407
0
  // note: `messages` will borrow the underlying buffer, but this is okay
408
0
  // because reverse destruction order means `messages` will be destroyed prior
409
0
  // to `array`/`data`.
410
0
  SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
411
0
  mActor->SendPostMessages(messages);
412
0
}
413
414
void
415
MessagePort::Start()
416
0
{
417
0
  if (mMessageQueueEnabled) {
418
0
    return;
419
0
  }
420
0
421
0
  mMessageQueueEnabled = true;
422
0
  Dispatch();
423
0
}
424
425
void
426
MessagePort::Dispatch()
427
0
{
428
0
  if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
429
0
    return;
430
0
  }
431
0
432
0
  switch (mState) {
433
0
    case eStateUnshippedEntangled:
434
0
      // Everything is fine here. We have messages because the other
435
0
      // port populates our queue directly.
436
0
      break;
437
0
438
0
    case eStateEntangling:
439
0
      // Everything is fine here as well. We have messages because the other
440
0
      // port populated our queue directly when we were in the
441
0
      // eStateUnshippedEntangled state.
442
0
      break;
443
0
444
0
    case eStateEntanglingForDisentangle:
445
0
      // Here we don't want to ship messages because these messages must be
446
0
      // delivered by the cloned version of this one. They will be sent in the
447
0
      // SendDisentangle().
448
0
      return;
449
0
450
0
    case eStateEntanglingForClose:
451
0
      // We still want to deliver messages if we are closing. These messages
452
0
      // are here from the previous eStateUnshippedEntangled state.
453
0
      break;
454
0
455
0
    case eStateEntangled:
456
0
      // This port is up and running.
457
0
      break;
458
0
459
0
    case eStateDisentangling:
460
0
      // If we are in the process to disentangle the port, we cannot dispatch
461
0
      // messages. They will be sent to the cloned version of this port via
462
0
      // SendDisentangle();
463
0
      return;
464
0
465
0
    case eStateDisentangled:
466
0
      MOZ_CRASH("This cannot happen.");
467
0
      // It cannot happen because Disentangle should take off all the pending
468
0
      // messages.
469
0
      break;
470
0
471
0
    case eStateDisentangledForClose:
472
0
      // If we are here is because the port has been closed. We can still
473
0
      // process the pending messages.
474
0
      break;
475
0
  }
476
0
477
0
  RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
478
0
  mMessages.RemoveElementAt(0);
479
0
480
0
  mPostMessageRunnable = new PostMessageRunnable(this, data);
481
0
482
0
  nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
483
0
  if (NS_IsMainThread() && global) {
484
0
    MOZ_ALWAYS_SUCCEEDS(global->Dispatch(TaskCategory::Other, do_AddRef(mPostMessageRunnable)));
485
0
    return;
486
0
  }
487
0
488
0
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
489
0
}
490
491
void
492
MessagePort::Close()
493
0
{
494
0
  mHasBeenTransferredOrClosed = true;
495
0
  CloseInternal(true /* aSoftly */);
496
0
}
497
498
void
499
MessagePort::CloseForced()
500
0
{
501
0
  CloseInternal(false /* aSoftly */);
502
0
}
503
504
void
505
MessagePort::CloseInternal(bool aSoftly)
506
0
{
507
0
  // If we have some messages to send but we don't want a 'soft' close, we have
508
0
  // to flush them now.
509
0
  if (!aSoftly) {
510
0
    mMessages.Clear();
511
0
  }
512
0
513
0
  if (mState == eStateUnshippedEntangled) {
514
0
    MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
515
0
516
0
    // This avoids loops.
517
0
    RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
518
0
519
0
    mState = eStateDisentangledForClose;
520
0
    port->CloseInternal(aSoftly);
521
0
522
0
    UpdateMustKeepAlive();
523
0
    return;
524
0
  }
525
0
526
0
  // Not entangled yet, we have to wait.
527
0
  if (mState == eStateEntangling) {
528
0
    mState = eStateEntanglingForClose;
529
0
    return;
530
0
  }
531
0
532
0
  // Not entangled but already cloned or closed
533
0
  if (mState == eStateEntanglingForDisentangle ||
534
0
      mState == eStateEntanglingForClose) {
535
0
    return;
536
0
  }
537
0
538
0
  // Maybe we were already closing the port but softly. In this case we call
539
0
  // UpdateMustKeepAlive() to consider the empty pending message queue.
540
0
  if (mState == eStateDisentangledForClose && !aSoftly) {
541
0
    UpdateMustKeepAlive();
542
0
    return;
543
0
  }
544
0
545
0
  if (mState > eStateEntangled) {
546
0
    return;
547
0
  }
548
0
549
0
  // We don't care about stopping the sending of messages because from now all
550
0
  // the incoming messages will be ignored.
551
0
  mState = eStateDisentangledForClose;
552
0
553
0
  MOZ_ASSERT(mActor);
554
0
555
0
  mActor->SendClose();
556
0
  mActor->SetPort(nullptr);
557
0
  mActor = nullptr;
558
0
559
0
  UpdateMustKeepAlive();
560
0
}
561
562
EventHandlerNonNull*
563
MessagePort::GetOnmessage()
564
0
{
565
0
  return GetEventHandler(nsGkAtoms::onmessage);
566
0
}
567
568
void
569
MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
570
0
{
571
0
  SetEventHandler(nsGkAtoms::onmessage, aCallback);
572
0
573
0
  // When using onmessage, the call to start() is implied.
574
0
  Start();
575
0
}
576
577
// This method is called when the PMessagePortChild actor is entangled to
578
// another actor. It receives a list of messages to be dispatch. It can be that
579
// we were waiting for this entangling step in order to disentangle the port or
580
// to close it.
581
void
582
MessagePort::Entangled(nsTArray<ClonedMessageData>& aMessages)
583
0
{
584
0
  MOZ_ASSERT(mState == eStateEntangling ||
585
0
             mState == eStateEntanglingForDisentangle ||
586
0
             mState == eStateEntanglingForClose);
587
0
588
0
  State oldState = mState;
589
0
  mState = eStateEntangled;
590
0
591
0
  // If we have pending messages, these have to be sent.
592
0
  if (!mMessagesForTheOtherPort.IsEmpty()) {
593
0
    {
594
0
      nsTArray<ClonedMessageData> messages;
595
0
      SharedMessagePortMessage::FromSharedToMessagesChild(mActor,
596
0
                                                          mMessagesForTheOtherPort,
597
0
                                                          messages);
598
0
      mActor->SendPostMessages(messages);
599
0
    }
600
0
    // Because `messages` borrow the underlying JSStructuredCloneData buffers,
601
0
    // only clear after `messages` have gone out of scope.
602
0
    mMessagesForTheOtherPort.Clear();
603
0
  }
604
0
605
0
  // We must convert the messages into SharedMessagePortMessages to avoid leaks.
606
0
  FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
607
0
  if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
608
0
                                                                      data))) {
609
0
    DispatchError();
610
0
    return;
611
0
  }
612
0
613
0
  // If the next step is to close the port, we do it ignoring the received
614
0
  // messages.
615
0
  if (oldState == eStateEntanglingForClose) {
616
0
    CloseForced();
617
0
    return;
618
0
  }
619
0
620
0
  mMessages.AppendElements(data);
621
0
622
0
  // We were waiting for the entangling callback in order to disentangle this
623
0
  // port immediately after.
624
0
  if (oldState == eStateEntanglingForDisentangle) {
625
0
    StartDisentangling();
626
0
    return;
627
0
  }
628
0
629
0
  Dispatch();
630
0
}
631
632
void
633
MessagePort::StartDisentangling()
634
0
{
635
0
  MOZ_ASSERT(mActor);
636
0
  MOZ_ASSERT(mState == eStateEntangled);
637
0
638
0
  mState = eStateDisentangling;
639
0
640
0
  // Sending this message we communicate to the parent actor that we don't want
641
0
  // to receive any new messages. It is possible that a message has been
642
0
  // already sent but not received yet. So we have to collect all of them and
643
0
  // we send them in the SendDispatch() request.
644
0
  mActor->SendStopSendingData();
645
0
}
646
647
void
648
MessagePort::MessagesReceived(nsTArray<ClonedMessageData>& aMessages)
649
0
{
650
0
  MOZ_ASSERT(mState == eStateEntangled ||
651
0
             mState == eStateDisentangling ||
652
0
             // This last step can happen only if Close() has been called
653
0
             // manually. At this point SendClose() is sent but we can still
654
0
             // receive something until the Closing request is processed.
655
0
             mState == eStateDisentangledForClose);
656
0
  MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
657
0
658
0
  RemoveDocFromBFCache();
659
0
660
0
  FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
661
0
  if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
662
0
                                                                      data))) {
663
0
    DispatchError();
664
0
    return;
665
0
  }
666
0
667
0
  mMessages.AppendElements(data);
668
0
669
0
  if (mState == eStateEntangled) {
670
0
    Dispatch();
671
0
  }
672
0
}
673
674
void
675
MessagePort::StopSendingDataConfirmed()
676
0
{
677
0
  MOZ_ASSERT(mState == eStateDisentangling);
678
0
  MOZ_ASSERT(mActor);
679
0
680
0
  Disentangle();
681
0
}
682
683
void
684
MessagePort::Disentangle()
685
0
{
686
0
  MOZ_ASSERT(mState == eStateDisentangling);
687
0
  MOZ_ASSERT(mActor);
688
0
689
0
  mState = eStateDisentangled;
690
0
691
0
  {
692
0
    nsTArray<ClonedMessageData> messages;
693
0
    SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessages,
694
0
                                                        messages);
695
0
    mActor->SendDisentangle(messages);
696
0
  }
697
0
  // Only clear mMessages after the ClonedMessageData instances have gone out of
698
0
  // scope because they borrow mMessages' underlying JSStructuredCloneDatas.
699
0
  mMessages.Clear();
700
0
701
0
  mActor->SetPort(nullptr);
702
0
  mActor = nullptr;
703
0
704
0
  UpdateMustKeepAlive();
705
0
}
706
707
void
708
MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
709
0
{
710
0
  MOZ_ASSERT(mIdentifier);
711
0
  MOZ_ASSERT(!mHasBeenTransferredOrClosed);
712
0
713
0
  mHasBeenTransferredOrClosed = true;
714
0
715
0
  // We can clone a port that has already been transfered. In this case, on the
716
0
  // otherside will have a neutered port. Here we set neutered to true so that
717
0
  // we are safe in case a early return.
718
0
  aIdentifier.neutered() = true;
719
0
720
0
  if (mState > eStateEntangled) {
721
0
    return;
722
0
  }
723
0
724
0
  // We already have a 'next step'. We have to consider this port as already
725
0
  // cloned/closed/disentangled.
726
0
  if (mState == eStateEntanglingForDisentangle ||
727
0
      mState == eStateEntanglingForClose) {
728
0
    return;
729
0
  }
730
0
731
0
  aIdentifier.uuid() = mIdentifier->uuid();
732
0
  aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
733
0
  aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
734
0
  aIdentifier.neutered() = false;
735
0
736
0
  // We have to entangle first.
737
0
  if (mState == eStateUnshippedEntangled) {
738
0
    MOZ_ASSERT(mUnshippedEntangledPort);
739
0
    MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
740
0
741
0
    RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
742
0
743
0
    // Disconnect the entangled port and connect it to PBackground.
744
0
    if (!port->ConnectToPBackground()) {
745
0
      // We are probably shutting down. We cannot proceed.
746
0
      mState = eStateDisentangled;
747
0
      UpdateMustKeepAlive();
748
0
      return;
749
0
    }
750
0
751
0
    // In this case, we don't need to be connected to the PBackground service.
752
0
    if (mMessages.IsEmpty()) {
753
0
      aIdentifier.sequenceId() = mIdentifier->sequenceId();
754
0
755
0
      mState = eStateDisentangled;
756
0
      UpdateMustKeepAlive();
757
0
      return;
758
0
    }
759
0
760
0
    // Register this component to PBackground.
761
0
    if (!ConnectToPBackground()) {
762
0
      // We are probably shutting down. We cannot proceed.
763
0
      return;
764
0
    }
765
0
766
0
    mState = eStateEntanglingForDisentangle;
767
0
    return;
768
0
  }
769
0
770
0
  // Not entangled yet, we have to wait.
771
0
  if (mState == eStateEntangling) {
772
0
    mState = eStateEntanglingForDisentangle;
773
0
    return;
774
0
  }
775
0
776
0
  MOZ_ASSERT(mState == eStateEntangled);
777
0
  StartDisentangling();
778
0
}
779
780
void
781
MessagePort::Closed()
782
0
{
783
0
  if (mState >= eStateDisentangled) {
784
0
    return;
785
0
  }
786
0
787
0
  mState = eStateDisentangledForClose;
788
0
789
0
  if (mActor) {
790
0
    mActor->SetPort(nullptr);
791
0
    mActor = nullptr;
792
0
  }
793
0
794
0
  UpdateMustKeepAlive();
795
0
}
796
797
bool
798
MessagePort::ConnectToPBackground()
799
0
{
800
0
  RefPtr<MessagePort> self = this;
801
0
  auto raii = MakeScopeExit([self] {
802
0
    self->mState = eStateDisentangled;
803
0
    self->UpdateMustKeepAlive();
804
0
  });
805
0
806
0
  mozilla::ipc::PBackgroundChild* actorChild =
807
0
    mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
808
0
  if (NS_WARN_IF(!actorChild)) {
809
0
    return false;
810
0
  }
811
0
812
0
  PMessagePortChild* actor =
813
0
    actorChild->SendPMessagePortConstructor(mIdentifier->uuid(),
814
0
                                            mIdentifier->destinationUuid(),
815
0
                                            mIdentifier->sequenceId());
816
0
  if (NS_WARN_IF(!actor)) {
817
0
    return false;
818
0
  }
819
0
820
0
  mActor = static_cast<MessagePortChild*>(actor);
821
0
  MOZ_ASSERT(mActor);
822
0
823
0
  mActor->SetPort(this);
824
0
  mState = eStateEntangling;
825
0
826
0
  raii.release();
827
0
  return true;
828
0
}
829
830
void
831
MessagePort::UpdateMustKeepAlive()
832
0
{
833
0
  if (mState >= eStateDisentangled &&
834
0
      mMessages.IsEmpty() &&
835
0
      mIsKeptAlive) {
836
0
    mIsKeptAlive = false;
837
0
838
0
    // The DTOR of this WorkerRef will release the worker for us.
839
0
    mWorkerRef = nullptr;
840
0
841
0
    Release();
842
0
    return;
843
0
  }
844
0
845
0
  if (mState < eStateDisentangled && !mIsKeptAlive) {
846
0
    mIsKeptAlive = true;
847
0
    AddRef();
848
0
  }
849
0
}
850
851
void
852
MessagePort::DisconnectFromOwner()
853
0
{
854
0
  CloseForced();
855
0
  DOMEventTargetHelper::DisconnectFromOwner();
856
0
}
857
858
void
859
MessagePort::RemoveDocFromBFCache()
860
0
{
861
0
  if (!NS_IsMainThread()) {
862
0
    return;
863
0
  }
864
0
865
0
  nsPIDOMWindowInner* window = GetOwner();
866
0
  if (!window) {
867
0
    return;
868
0
  }
869
0
870
0
  nsIDocument* doc = window->GetExtantDoc();
871
0
  if (!doc) {
872
0
    return;
873
0
  }
874
0
875
0
  nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
876
0
  if (!bfCacheEntry) {
877
0
    return;
878
0
  }
879
0
880
0
  bfCacheEntry->RemoveFromBFCacheSync();
881
0
}
882
883
/* static */ void
884
MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier)
885
0
{
886
0
  mozilla::ipc::PBackgroundChild* actorChild =
887
0
    mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
888
0
  if (NS_WARN_IF(!actorChild)) {
889
0
    MOZ_CRASH("Failed to create a PBackgroundChild actor!");
890
0
  }
891
0
892
0
  Unused << actorChild->SendMessagePortForceClose(aIdentifier.uuid(),
893
0
                                                  aIdentifier.destinationUuid(),
894
0
                                                  aIdentifier.sequenceId());
895
0
}
896
897
void
898
MessagePort::DispatchError()
899
0
{
900
0
  nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
901
0
902
0
  AutoJSAPI jsapi;
903
0
  if (!globalObject || !jsapi.Init(globalObject)) {
904
0
    NS_WARNING("Failed to initialize AutoJSAPI object.");
905
0
    return;
906
0
  }
907
0
908
0
  RootedDictionary<MessageEventInit> init(jsapi.cx());
909
0
  init.mBubbles = false;
910
0
  init.mCancelable = false;
911
0
912
0
  RefPtr<Event> event =
913
0
    MessageEvent::Constructor(this, NS_LITERAL_STRING("messageerror"), init);
914
0
  event->SetTrusted(true);
915
0
916
0
  DispatchEvent(*event);
917
0
}
918
919
} // namespace dom
920
} // namespace mozilla