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