Source code

Revision control

Other Tools

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