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