Bug 1750871 - run mochitest-remote on fission everywhere. r=releng-reviewers,aki
[gecko.git] / dom / messagechannel / MessagePort.cpp
blob3acac4f4ba8f3c2f768c002110667f6d3391b7ea
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/. */
7 #include "MessagePort.h"
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/RootedDictionary.h"
20 #include "mozilla/dom/ScriptSettings.h"
21 #include "mozilla/dom/StructuredCloneTags.h"
22 #include "mozilla/dom/WorkerCommon.h"
23 #include "mozilla/dom/WorkerRef.h"
24 #include "mozilla/dom/WorkerScope.h"
25 #include "mozilla/ipc/BackgroundChild.h"
26 #include "mozilla/dom/RefMessageBodyService.h"
27 #include "mozilla/dom/SharedMessageBody.h"
28 #include "mozilla/ipc/PBackgroundChild.h"
29 #include "mozilla/MessagePortTimelineMarker.h"
30 #include "mozilla/ScopeExit.h"
31 #include "mozilla/TimelineConsumers.h"
32 #include "mozilla/TimelineMarker.h"
33 #include "mozilla/Unused.h"
34 #include "nsContentUtils.h"
35 #include "nsGlobalWindow.h"
36 #include "nsPresContext.h"
38 #ifdef XP_WIN
39 # undef PostMessage
40 #endif
42 namespace mozilla::dom {
44 void UniqueMessagePortId::ForceClose() {
45 if (!mIdentifier.neutered()) {
46 MessagePort::ForceClose(mIdentifier);
47 mIdentifier.neutered() = true;
51 class PostMessageRunnable final : public CancelableRunnable {
52 friend class MessagePort;
54 public:
55 PostMessageRunnable(MessagePort* aPort, SharedMessageBody* aData)
56 : CancelableRunnable("dom::PostMessageRunnable"),
57 mPort(aPort),
58 mData(aData) {
59 MOZ_ASSERT(aPort);
60 MOZ_ASSERT(aData);
63 NS_IMETHOD
64 Run() override {
65 NS_ASSERT_OWNINGTHREAD(Runnable);
67 // The port can be cycle collected while this runnable is pending in
68 // the event queue.
69 if (!mPort) {
70 return NS_OK;
73 MOZ_ASSERT(mPort->mPostMessageRunnable == this);
75 DispatchMessage();
77 // We must check if we were waiting for this message in order to shutdown
78 // the port.
79 mPort->UpdateMustKeepAlive();
81 mPort->mPostMessageRunnable = nullptr;
82 mPort->Dispatch();
84 return NS_OK;
87 nsresult Cancel() override {
88 NS_ASSERT_OWNINGTHREAD(Runnable);
90 mPort = nullptr;
91 mData = nullptr;
92 return NS_OK;
95 private:
96 void DispatchMessage() const {
97 NS_ASSERT_OWNINGTHREAD(Runnable);
99 if (NS_FAILED(mPort->CheckCurrentGlobalCorrectness())) {
100 return;
103 nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
105 AutoJSAPI jsapi;
106 if (!globalObject || !jsapi.Init(globalObject)) {
107 NS_WARNING("Failed to initialize AutoJSAPI object.");
108 return;
111 JSContext* cx = jsapi.cx();
113 IgnoredErrorResult rv;
114 JS::Rooted<JS::Value> value(cx);
116 UniquePtr<AbstractTimelineMarker> start;
117 UniquePtr<AbstractTimelineMarker> end;
118 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
119 bool isTimelineRecording = timelines && !timelines->IsEmpty();
121 if (isTimelineRecording) {
122 start = MakeUnique<MessagePortTimelineMarker>(
123 ProfileTimelineMessagePortOperationType::DeserializeData,
124 MarkerTracingType::START);
127 mData->Read(cx, &value, mPort->mRefMessageBodyService,
128 SharedMessageBody::ReadMethod::StealRefMessageBody, rv);
130 if (isTimelineRecording) {
131 end = MakeUnique<MessagePortTimelineMarker>(
132 ProfileTimelineMessagePortOperationType::DeserializeData,
133 MarkerTracingType::END);
134 timelines->AddMarkerForAllObservedDocShells(start);
135 timelines->AddMarkerForAllObservedDocShells(end);
138 if (NS_WARN_IF(rv.Failed())) {
139 JS_ClearPendingException(cx);
140 mPort->DispatchError();
141 return;
144 // Create the event
145 nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
146 do_QueryInterface(mPort->GetOwner());
147 RefPtr<MessageEvent> event =
148 new MessageEvent(eventTarget, nullptr, nullptr);
150 Sequence<OwningNonNull<MessagePort>> ports;
151 if (!mData->TakeTransferredPortsAsSequence(ports)) {
152 mPort->DispatchError();
153 return;
156 event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
157 Cancelable::eNo, value, u""_ns, u""_ns, nullptr,
158 ports);
159 event->SetTrusted(true);
161 mPort->DispatchEvent(*event);
164 private:
165 ~PostMessageRunnable() = default;
167 RefPtr<MessagePort> mPort;
168 RefPtr<SharedMessageBody> mData;
171 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
173 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
174 DOMEventTargetHelper)
175 if (tmp->mPostMessageRunnable) {
176 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
179 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
181 tmp->CloseForced();
182 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
185 DOMEventTargetHelper)
186 if (tmp->mPostMessageRunnable) {
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
191 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
193 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePort)
194 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
196 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
197 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
199 MessagePort::MessagePort(nsIGlobalObject* aGlobal, State aState)
200 : DOMEventTargetHelper(aGlobal),
201 mRefMessageBodyService(RefMessageBodyService::GetOrCreate()),
202 mState(aState),
203 mMessageQueueEnabled(false),
204 mIsKeptAlive(false),
205 mHasBeenTransferredOrClosed(false) {
206 MOZ_ASSERT(aGlobal);
208 mIdentifier = MakeUnique<MessagePortIdentifier>();
209 mIdentifier->neutered() = true;
210 mIdentifier->sequenceId() = 0;
213 MessagePort::~MessagePort() {
214 CloseForced();
215 MOZ_ASSERT(!mWorkerRef);
218 /* static */
219 already_AddRefed<MessagePort> MessagePort::Create(nsIGlobalObject* aGlobal,
220 const nsID& aUUID,
221 const nsID& aDestinationUUID,
222 ErrorResult& aRv) {
223 MOZ_ASSERT(aGlobal);
225 RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateUnshippedEntangled);
226 mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
227 false /* Neutered */, aRv);
228 return mp.forget();
231 /* static */
232 already_AddRefed<MessagePort> MessagePort::Create(
233 nsIGlobalObject* aGlobal, UniqueMessagePortId& aIdentifier,
234 ErrorResult& aRv) {
235 MOZ_ASSERT(aGlobal);
237 RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateEntangling);
238 mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
239 aIdentifier.sequenceId(), aIdentifier.neutered(), aRv);
240 aIdentifier.neutered() = true;
241 return mp.forget();
244 void MessagePort::UnshippedEntangle(MessagePort* aEntangledPort) {
245 MOZ_DIAGNOSTIC_ASSERT(aEntangledPort);
246 MOZ_DIAGNOSTIC_ASSERT(!mUnshippedEntangledPort);
248 mUnshippedEntangledPort = aEntangledPort;
251 void MessagePort::Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
252 uint32_t aSequenceID, bool aNeutered,
253 ErrorResult& aRv) {
254 MOZ_ASSERT(mIdentifier);
255 mIdentifier->uuid() = aUUID;
256 mIdentifier->destinationUuid() = aDestinationUUID;
257 mIdentifier->sequenceId() = aSequenceID;
259 if (aNeutered) {
260 // If this port is neutered we don't want to keep it alive artificially nor
261 // we want to add listeners or WorkerRefs.
262 mState = eStateDisentangled;
263 return;
266 if (mState == eStateEntangling) {
267 if (!ConnectToPBackground()) {
268 aRv.Throw(NS_ERROR_FAILURE);
269 return;
271 } else {
272 MOZ_ASSERT(mState == eStateUnshippedEntangled);
275 // The port has to keep itself alive until it's entangled.
276 UpdateMustKeepAlive();
278 if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
279 RefPtr<MessagePort> self = this;
281 // When the callback is executed, we cannot process messages anymore because
282 // we cannot dispatch new runnables. Let's force a Close().
283 RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
284 workerPrivate, "MessagePort", [self]() { self->CloseForced(); });
285 if (NS_WARN_IF(!strongWorkerRef)) {
286 // The worker is shutting down.
287 mState = eStateDisentangled;
288 UpdateMustKeepAlive();
289 aRv.Throw(NS_ERROR_FAILURE);
290 return;
293 MOZ_ASSERT(!mWorkerRef);
294 mWorkerRef = std::move(strongWorkerRef);
298 JSObject* MessagePort::WrapObject(JSContext* aCx,
299 JS::Handle<JSObject*> aGivenProto) {
300 return MessagePort_Binding::Wrap(aCx, this, aGivenProto);
303 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
304 const Sequence<JSObject*>& aTransferable,
305 ErrorResult& aRv) {
306 // We *must* clone the data here, or the JS::Value could be modified
307 // by script
309 // Here we want to check if the transerable object list contains
310 // this port.
311 for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
312 JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
313 if (!object) {
314 continue;
317 MessagePort* port = nullptr;
318 nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
319 if (NS_SUCCEEDED(rv) && port == this) {
320 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
321 return;
325 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
327 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
328 &transferable);
329 if (NS_WARN_IF(aRv.Failed())) {
330 return;
333 Maybe<nsID> agentClusterId;
334 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
335 if (global) {
336 agentClusterId = global->GetAgentClusterId();
339 RefPtr<SharedMessageBody> data = new SharedMessageBody(
340 StructuredCloneHolder::TransferringSupported, agentClusterId);
342 UniquePtr<AbstractTimelineMarker> start;
343 UniquePtr<AbstractTimelineMarker> end;
344 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
345 bool isTimelineRecording = timelines && !timelines->IsEmpty();
347 if (isTimelineRecording) {
348 start = MakeUnique<MessagePortTimelineMarker>(
349 ProfileTimelineMessagePortOperationType::SerializeData,
350 MarkerTracingType::START);
353 data->Write(aCx, aMessage, transferable, mIdentifier->uuid(),
354 mRefMessageBodyService, aRv);
356 if (isTimelineRecording) {
357 end = MakeUnique<MessagePortTimelineMarker>(
358 ProfileTimelineMessagePortOperationType::SerializeData,
359 MarkerTracingType::END);
360 timelines->AddMarkerForAllObservedDocShells(start);
361 timelines->AddMarkerForAllObservedDocShells(end);
364 if (NS_WARN_IF(aRv.Failed())) {
365 return;
368 // This message has to be ignored.
369 if (mState > eStateEntangled) {
370 return;
373 // If we are unshipped we are connected to the other port on the same thread.
374 if (mState == eStateUnshippedEntangled) {
375 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
376 mUnshippedEntangledPort->mMessages.AppendElement(data);
377 mUnshippedEntangledPort->Dispatch();
378 return;
381 // Not entangled yet, but already closed/disentangled.
382 if (mState == eStateEntanglingForDisentangle ||
383 mState == eStateEntanglingForClose) {
384 return;
387 RemoveDocFromBFCache();
389 // Not entangled yet.
390 if (mState == eStateEntangling) {
391 mMessagesForTheOtherPort.AppendElement(data);
392 return;
395 MOZ_ASSERT(mActor);
396 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
398 AutoTArray<RefPtr<SharedMessageBody>, 1> array;
399 array.AppendElement(data);
401 AutoTArray<MessageData, 1> messages;
402 // note: `messages` will borrow the underlying buffer, but this is okay
403 // because reverse destruction order means `messages` will be destroyed prior
404 // to `array`/`data`.
405 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), array,
406 messages);
407 mActor->SendPostMessages(messages);
410 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
411 const StructuredSerializeOptions& aOptions,
412 ErrorResult& aRv) {
413 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
416 void MessagePort::Start() {
417 if (mMessageQueueEnabled) {
418 return;
421 mMessageQueueEnabled = true;
422 Dispatch();
425 void MessagePort::Dispatch() {
426 if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
427 return;
430 switch (mState) {
431 case eStateUnshippedEntangled:
432 // Everything is fine here. We have messages because the other
433 // port populates our queue directly.
434 break;
436 case eStateEntangling:
437 // Everything is fine here as well. We have messages because the other
438 // port populated our queue directly when we were in the
439 // eStateUnshippedEntangled state.
440 break;
442 case eStateEntanglingForDisentangle:
443 // Here we don't want to ship messages because these messages must be
444 // delivered by the cloned version of this one. They will be sent in the
445 // SendDisentangle().
446 return;
448 case eStateEntanglingForClose:
449 // We still want to deliver messages if we are closing. These messages
450 // are here from the previous eStateUnshippedEntangled state.
451 break;
453 case eStateEntangled:
454 // This port is up and running.
455 break;
457 case eStateDisentangling:
458 // If we are in the process to disentangle the port, we cannot dispatch
459 // messages. They will be sent to the cloned version of this port via
460 // SendDisentangle();
461 return;
463 case eStateDisentangled:
464 MOZ_CRASH("This cannot happen.");
465 // It cannot happen because Disentangle should take off all the pending
466 // messages.
467 break;
469 case eStateDisentangledForClose:
470 // If we are here is because the port has been closed. We can still
471 // process the pending messages.
472 break;
475 RefPtr<SharedMessageBody> data = mMessages.ElementAt(0);
476 mMessages.RemoveElementAt(0);
478 mPostMessageRunnable = new PostMessageRunnable(this, data);
480 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
481 if (NS_IsMainThread() && global) {
482 MOZ_ALWAYS_SUCCEEDS(
483 global->Dispatch(TaskCategory::Other, do_AddRef(mPostMessageRunnable)));
484 return;
487 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
490 void MessagePort::Close() {
491 mHasBeenTransferredOrClosed = true;
492 CloseInternal(true /* aSoftly */);
495 void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); }
497 void MessagePort::CloseInternal(bool aSoftly) {
498 // If we have some messages to send but we don't want a 'soft' close, we have
499 // to flush them now.
500 if (!aSoftly) {
501 mMessages.Clear();
504 // Let's inform the RefMessageBodyService that any our shared messages are
505 // now invalid.
506 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
508 if (mState == eStateUnshippedEntangled) {
509 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
511 // This avoids loops.
512 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
514 mState = eStateDisentangledForClose;
515 port->CloseInternal(aSoftly);
517 UpdateMustKeepAlive();
518 return;
521 // Not entangled yet, we have to wait.
522 if (mState == eStateEntangling) {
523 mState = eStateEntanglingForClose;
524 return;
527 // Not entangled but already cloned or closed
528 if (mState == eStateEntanglingForDisentangle ||
529 mState == eStateEntanglingForClose) {
530 return;
533 // Maybe we were already closing the port but softly. In this case we call
534 // UpdateMustKeepAlive() to consider the empty pending message queue.
535 if (mState == eStateDisentangledForClose && !aSoftly) {
536 UpdateMustKeepAlive();
537 return;
540 if (mState > eStateEntangled) {
541 return;
544 // We don't care about stopping the sending of messages because from now all
545 // the incoming messages will be ignored.
546 mState = eStateDisentangledForClose;
548 MOZ_ASSERT(mActor);
550 mActor->SendClose();
551 mActor->SetPort(nullptr);
552 mActor = nullptr;
554 UpdateMustKeepAlive();
557 EventHandlerNonNull* MessagePort::GetOnmessage() {
558 return GetEventHandler(nsGkAtoms::onmessage);
561 void MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) {
562 SetEventHandler(nsGkAtoms::onmessage, aCallback);
564 // When using onmessage, the call to start() is implied.
565 Start();
568 // This method is called when the PMessagePortChild actor is entangled to
569 // another actor. It receives a list of messages to be dispatch. It can be that
570 // we were waiting for this entangling step in order to disentangle the port or
571 // to close it.
572 void MessagePort::Entangled(nsTArray<MessageData>& aMessages) {
573 MOZ_ASSERT(mState == eStateEntangling ||
574 mState == eStateEntanglingForDisentangle ||
575 mState == eStateEntanglingForClose);
577 State oldState = mState;
578 mState = eStateEntangled;
580 // If we have pending messages, these have to be sent.
581 if (!mMessagesForTheOtherPort.IsEmpty()) {
583 nsTArray<MessageData> messages;
584 SharedMessageBody::FromSharedToMessagesChild(
585 mActor->Manager(), mMessagesForTheOtherPort, messages);
586 mActor->SendPostMessages(messages);
588 // Because `messages` borrow the underlying JSStructuredCloneData buffers,
589 // only clear after `messages` have gone out of scope.
590 mMessagesForTheOtherPort.Clear();
593 // We must convert the messages into SharedMessageBodys to avoid leaks.
594 FallibleTArray<RefPtr<SharedMessageBody>> data;
595 if (NS_WARN_IF(
596 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
597 DispatchError();
598 return;
601 // If the next step is to close the port, we do it ignoring the received
602 // messages.
603 if (oldState == eStateEntanglingForClose) {
604 CloseForced();
605 return;
608 mMessages.AppendElements(data);
610 // We were waiting for the entangling callback in order to disentangle this
611 // port immediately after.
612 if (oldState == eStateEntanglingForDisentangle) {
613 StartDisentangling();
614 return;
617 Dispatch();
620 void MessagePort::StartDisentangling() {
621 MOZ_ASSERT(mActor);
622 MOZ_ASSERT(mState == eStateEntangled);
624 mState = eStateDisentangling;
626 // Sending this message we communicate to the parent actor that we don't want
627 // to receive any new messages. It is possible that a message has been
628 // already sent but not received yet. So we have to collect all of them and
629 // we send them in the SendDispatch() request.
630 mActor->SendStopSendingData();
633 void MessagePort::MessagesReceived(nsTArray<MessageData>& aMessages) {
634 MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling ||
635 // This last step can happen only if Close() has been called
636 // manually. At this point SendClose() is sent but we can still
637 // receive something until the Closing request is processed.
638 mState == eStateDisentangledForClose);
639 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
641 RemoveDocFromBFCache();
643 FallibleTArray<RefPtr<SharedMessageBody>> data;
644 if (NS_WARN_IF(
645 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
646 DispatchError();
647 return;
650 mMessages.AppendElements(data);
652 if (mState == eStateEntangled) {
653 Dispatch();
657 void MessagePort::StopSendingDataConfirmed() {
658 MOZ_ASSERT(mState == eStateDisentangling);
659 MOZ_ASSERT(mActor);
661 Disentangle();
664 void MessagePort::Disentangle() {
665 MOZ_ASSERT(mState == eStateDisentangling);
666 MOZ_ASSERT(mActor);
668 mState = eStateDisentangled;
671 nsTArray<MessageData> messages;
672 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), mMessages,
673 messages);
674 mActor->SendDisentangle(messages);
677 // Let's inform the RefMessageBodyService that any our shared messages are
678 // now invalid.
679 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
681 // Only clear mMessages after the MessageData instances have gone out of scope
682 // because they borrow mMessages' underlying JSStructuredCloneDatas.
683 mMessages.Clear();
685 mActor->SetPort(nullptr);
686 mActor = nullptr;
688 UpdateMustKeepAlive();
691 void MessagePort::CloneAndDisentangle(UniqueMessagePortId& aIdentifier) {
692 MOZ_ASSERT(mIdentifier);
693 MOZ_ASSERT(!mHasBeenTransferredOrClosed);
695 mHasBeenTransferredOrClosed = true;
697 // We can clone a port that has already been transfered. In this case, on the
698 // otherside will have a neutered port. Here we set neutered to true so that
699 // we are safe in case a early return.
700 aIdentifier.neutered() = true;
702 if (mState > eStateEntangled) {
703 return;
706 // We already have a 'next step'. We have to consider this port as already
707 // cloned/closed/disentangled.
708 if (mState == eStateEntanglingForDisentangle ||
709 mState == eStateEntanglingForClose) {
710 return;
713 aIdentifier.uuid() = mIdentifier->uuid();
714 aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
715 aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
716 aIdentifier.neutered() = false;
718 // We have to entangle first.
719 if (mState == eStateUnshippedEntangled) {
720 MOZ_ASSERT(mUnshippedEntangledPort);
721 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
723 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
725 // Disconnect the entangled port and connect it to PBackground.
726 if (!port->ConnectToPBackground()) {
727 // We are probably shutting down. We cannot proceed.
728 mState = eStateDisentangled;
729 UpdateMustKeepAlive();
730 return;
733 // In this case, we don't need to be connected to the PBackground service.
734 if (mMessages.IsEmpty()) {
735 aIdentifier.sequenceId() = mIdentifier->sequenceId();
737 mState = eStateDisentangled;
738 UpdateMustKeepAlive();
739 return;
742 // Register this component to PBackground.
743 if (!ConnectToPBackground()) {
744 // We are probably shutting down. We cannot proceed.
745 return;
748 mState = eStateEntanglingForDisentangle;
749 return;
752 // Not entangled yet, we have to wait.
753 if (mState == eStateEntangling) {
754 mState = eStateEntanglingForDisentangle;
755 return;
758 MOZ_ASSERT(mState == eStateEntangled);
759 StartDisentangling();
762 void MessagePort::Closed() {
763 if (mState >= eStateDisentangled) {
764 return;
767 mState = eStateDisentangledForClose;
769 if (mActor) {
770 mActor->SetPort(nullptr);
771 mActor = nullptr;
774 UpdateMustKeepAlive();
777 bool MessagePort::ConnectToPBackground() {
778 RefPtr<MessagePort> self = this;
779 auto raii = MakeScopeExit([self] {
780 self->mState = eStateDisentangled;
781 self->UpdateMustKeepAlive();
784 mozilla::ipc::PBackgroundChild* actorChild =
785 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
786 if (NS_WARN_IF(!actorChild)) {
787 return false;
790 PMessagePortChild* actor = actorChild->SendPMessagePortConstructor(
791 mIdentifier->uuid(), mIdentifier->destinationUuid(),
792 mIdentifier->sequenceId());
793 if (NS_WARN_IF(!actor)) {
794 return false;
797 mActor = static_cast<MessagePortChild*>(actor);
798 MOZ_ASSERT(mActor);
800 mActor->SetPort(this);
801 mState = eStateEntangling;
803 raii.release();
804 return true;
807 void MessagePort::UpdateMustKeepAlive() {
808 if (mState >= eStateDisentangled && mMessages.IsEmpty() && mIsKeptAlive) {
809 mIsKeptAlive = false;
811 // The DTOR of this WorkerRef will release the worker for us.
812 mWorkerRef = nullptr;
814 Release();
815 return;
818 if (mState < eStateDisentangled && !mIsKeptAlive) {
819 mIsKeptAlive = true;
820 AddRef();
824 void MessagePort::DisconnectFromOwner() {
825 CloseForced();
826 DOMEventTargetHelper::DisconnectFromOwner();
829 void MessagePort::RemoveDocFromBFCache() {
830 if (!NS_IsMainThread()) {
831 return;
834 if (nsPIDOMWindowInner* window = GetOwner()) {
835 window->RemoveFromBFCacheSync();
839 /* static */
840 void MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier) {
841 mozilla::ipc::PBackgroundChild* actorChild =
842 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
843 if (NS_WARN_IF(!actorChild)) {
844 MOZ_CRASH("Failed to create a PBackgroundChild actor!");
847 Unused << actorChild->SendMessagePortForceClose(aIdentifier.uuid(),
848 aIdentifier.destinationUuid(),
849 aIdentifier.sequenceId());
852 void MessagePort::DispatchError() {
853 nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
855 AutoJSAPI jsapi;
856 if (!globalObject || !jsapi.Init(globalObject)) {
857 NS_WARNING("Failed to initialize AutoJSAPI object.");
858 return;
861 RootedDictionary<MessageEventInit> init(jsapi.cx());
862 init.mBubbles = false;
863 init.mCancelable = false;
865 RefPtr<Event> event =
866 MessageEvent::Constructor(this, u"messageerror"_ns, init);
867 event->SetTrusted(true);
869 DispatchEvent(*event);
872 } // namespace mozilla::dom