Bug 1860712 [wpt PR 42705] - Port popover test to WPT and adjust for close requests...
[gecko.git] / dom / messagechannel / MessagePort.cpp
blob23c54cde258536355591240518848df77aba0cfc
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/ScopeExit.h"
30 #include "mozilla/Unused.h"
31 #include "nsContentUtils.h"
32 #include "nsPresContext.h"
34 #ifdef XP_WIN
35 # undef PostMessage
36 #endif
38 namespace mozilla::dom {
40 void UniqueMessagePortId::ForceClose() {
41 if (!mIdentifier.neutered()) {
42 MessagePort::ForceClose(mIdentifier);
43 mIdentifier.neutered() = true;
47 class PostMessageRunnable final : public CancelableRunnable {
48 friend class MessagePort;
50 public:
51 PostMessageRunnable(MessagePort* aPort, SharedMessageBody* aData)
52 : CancelableRunnable("dom::PostMessageRunnable"),
53 mPort(aPort),
54 mData(aData) {
55 MOZ_ASSERT(aPort);
56 MOZ_ASSERT(aData);
59 NS_IMETHOD
60 Run() override {
61 NS_ASSERT_OWNINGTHREAD(Runnable);
63 // The port can be cycle collected while this runnable is pending in
64 // the event queue.
65 if (!mPort) {
66 return NS_OK;
69 MOZ_ASSERT(mPort->mPostMessageRunnable == this);
71 DispatchMessage();
73 // We must check if we were waiting for this message in order to shutdown
74 // the port.
75 mPort->UpdateMustKeepAlive();
77 mPort->mPostMessageRunnable = nullptr;
78 mPort->Dispatch();
80 return NS_OK;
83 nsresult Cancel() override {
84 NS_ASSERT_OWNINGTHREAD(Runnable);
86 mPort = nullptr;
87 mData = nullptr;
88 return NS_OK;
91 private:
92 void DispatchMessage() const {
93 NS_ASSERT_OWNINGTHREAD(Runnable);
95 if (NS_FAILED(mPort->CheckCurrentGlobalCorrectness())) {
96 return;
99 nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
101 AutoJSAPI jsapi;
102 if (!globalObject || !jsapi.Init(globalObject)) {
103 NS_WARNING("Failed to initialize AutoJSAPI object.");
104 return;
107 JSContext* cx = jsapi.cx();
109 IgnoredErrorResult rv;
110 JS::Rooted<JS::Value> value(cx);
112 mData->Read(cx, &value, mPort->mRefMessageBodyService,
113 SharedMessageBody::ReadMethod::StealRefMessageBody, rv);
115 if (NS_WARN_IF(rv.Failed())) {
116 JS_ClearPendingException(cx);
117 mPort->DispatchError();
118 return;
121 // Create the event
122 nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
123 do_QueryInterface(mPort->GetOwner());
124 RefPtr<MessageEvent> event =
125 new MessageEvent(eventTarget, nullptr, nullptr);
127 Sequence<OwningNonNull<MessagePort>> ports;
128 if (!mData->TakeTransferredPortsAsSequence(ports)) {
129 mPort->DispatchError();
130 return;
133 event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
134 Cancelable::eNo, value, u""_ns, u""_ns, nullptr,
135 ports);
136 event->SetTrusted(true);
138 mPort->DispatchEvent(*event);
141 private:
142 ~PostMessageRunnable() = default;
144 RefPtr<MessagePort> mPort;
145 RefPtr<SharedMessageBody> mData;
148 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
150 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
151 DOMEventTargetHelper)
152 if (tmp->mPostMessageRunnable) {
153 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
156 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
158 tmp->CloseForced();
159 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
161 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
162 DOMEventTargetHelper)
163 if (tmp->mPostMessageRunnable) {
164 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
167 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
168 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
170 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePort)
171 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
173 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
174 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
176 MessagePort::MessagePort(nsIGlobalObject* aGlobal, State aState)
177 : DOMEventTargetHelper(aGlobal),
178 mRefMessageBodyService(RefMessageBodyService::GetOrCreate()),
179 mState(aState),
180 mMessageQueueEnabled(false),
181 mIsKeptAlive(false),
182 mHasBeenTransferredOrClosed(false) {
183 MOZ_ASSERT(aGlobal);
185 mIdentifier = MakeUnique<MessagePortIdentifier>();
186 mIdentifier->neutered() = true;
187 mIdentifier->sequenceId() = 0;
190 MessagePort::~MessagePort() {
191 CloseForced();
192 MOZ_ASSERT(!mActor);
193 if (mActor) {
194 mActor->SetPort(nullptr);
195 mActor = nullptr;
197 MOZ_ASSERT(!mWorkerRef);
200 /* static */
201 already_AddRefed<MessagePort> MessagePort::Create(nsIGlobalObject* aGlobal,
202 const nsID& aUUID,
203 const nsID& aDestinationUUID,
204 ErrorResult& aRv) {
205 MOZ_ASSERT(aGlobal);
207 RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateUnshippedEntangled);
208 mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
209 false /* Neutered */, aRv);
210 return mp.forget();
213 /* static */
214 already_AddRefed<MessagePort> MessagePort::Create(
215 nsIGlobalObject* aGlobal, UniqueMessagePortId& aIdentifier,
216 ErrorResult& aRv) {
217 MOZ_ASSERT(aGlobal);
219 RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateEntangling);
220 mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
221 aIdentifier.sequenceId(), aIdentifier.neutered(), aRv);
222 aIdentifier.neutered() = true;
223 return mp.forget();
226 void MessagePort::UnshippedEntangle(MessagePort* aEntangledPort) {
227 MOZ_DIAGNOSTIC_ASSERT(aEntangledPort);
228 MOZ_DIAGNOSTIC_ASSERT(!mUnshippedEntangledPort);
230 mUnshippedEntangledPort = aEntangledPort;
233 void MessagePort::Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
234 uint32_t aSequenceID, bool aNeutered,
235 ErrorResult& aRv) {
236 MOZ_ASSERT(mIdentifier);
237 mIdentifier->uuid() = aUUID;
238 mIdentifier->destinationUuid() = aDestinationUUID;
239 mIdentifier->sequenceId() = aSequenceID;
241 if (aNeutered) {
242 // If this port is neutered we don't want to keep it alive artificially nor
243 // we want to add listeners or WorkerRefs.
244 mState = eStateDisentangled;
245 return;
248 if (mState == eStateEntangling) {
249 if (!ConnectToPBackground()) {
250 aRv.Throw(NS_ERROR_FAILURE);
251 return;
253 } else {
254 MOZ_ASSERT(mState == eStateUnshippedEntangled);
257 // The port has to keep itself alive until it's entangled.
258 UpdateMustKeepAlive();
260 if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
261 RefPtr<MessagePort> self = this;
263 // When the callback is executed, we cannot process messages anymore because
264 // we cannot dispatch new runnables. Let's force a Close().
265 RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
266 workerPrivate, "MessagePort", [self]() { self->CloseForced(); });
267 if (NS_WARN_IF(!strongWorkerRef)) {
268 // The worker is shutting down.
269 CloseForced();
270 aRv.Throw(NS_ERROR_FAILURE);
271 return;
274 MOZ_ASSERT(!mWorkerRef);
275 mWorkerRef = std::move(strongWorkerRef);
279 JSObject* MessagePort::WrapObject(JSContext* aCx,
280 JS::Handle<JSObject*> aGivenProto) {
281 return MessagePort_Binding::Wrap(aCx, this, aGivenProto);
284 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
285 const Sequence<JSObject*>& aTransferable,
286 ErrorResult& aRv) {
287 // We *must* clone the data here, or the JS::Value could be modified
288 // by script
290 // Here we want to check if the transerable object list contains
291 // this port.
292 for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
293 JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
294 if (!object) {
295 continue;
298 MessagePort* port = nullptr;
299 nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
300 if (NS_SUCCEEDED(rv) && port == this) {
301 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
302 return;
306 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
308 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
309 &transferable);
310 if (NS_WARN_IF(aRv.Failed())) {
311 return;
314 Maybe<nsID> agentClusterId;
315 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
316 if (global) {
317 agentClusterId = global->GetAgentClusterId();
320 RefPtr<SharedMessageBody> data = new SharedMessageBody(
321 StructuredCloneHolder::TransferringSupported, agentClusterId);
323 data->Write(aCx, aMessage, transferable, mIdentifier->uuid(),
324 mRefMessageBodyService, aRv);
326 if (NS_WARN_IF(aRv.Failed())) {
327 return;
330 // This message has to be ignored.
331 if (mState > eStateEntangled) {
332 return;
335 // If we are unshipped we are connected to the other port on the same thread.
336 if (mState == eStateUnshippedEntangled) {
337 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
338 mUnshippedEntangledPort->mMessages.AppendElement(data);
339 mUnshippedEntangledPort->Dispatch();
340 return;
343 // Not entangled yet, but already closed/disentangled.
344 if (mState == eStateEntanglingForDisentangle ||
345 mState == eStateEntanglingForClose) {
346 return;
349 RemoveDocFromBFCache();
351 // Not entangled yet.
352 if (mState == eStateEntangling) {
353 mMessagesForTheOtherPort.AppendElement(data);
354 return;
357 MOZ_ASSERT(mActor);
358 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
360 AutoTArray<RefPtr<SharedMessageBody>, 1> array;
361 array.AppendElement(data);
363 AutoTArray<MessageData, 1> messages;
364 // note: `messages` will borrow the underlying buffer, but this is okay
365 // because reverse destruction order means `messages` will be destroyed prior
366 // to `array`/`data`.
367 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), array,
368 messages);
369 mActor->SendPostMessages(messages);
372 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
373 const StructuredSerializeOptions& aOptions,
374 ErrorResult& aRv) {
375 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
378 void MessagePort::Start() {
379 if (mMessageQueueEnabled) {
380 return;
383 mMessageQueueEnabled = true;
384 Dispatch();
387 void MessagePort::Dispatch() {
388 if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
389 return;
392 switch (mState) {
393 case eStateUnshippedEntangled:
394 // Everything is fine here. We have messages because the other
395 // port populates our queue directly.
396 break;
398 case eStateEntangling:
399 // Everything is fine here as well. We have messages because the other
400 // port populated our queue directly when we were in the
401 // eStateUnshippedEntangled state.
402 break;
404 case eStateEntanglingForDisentangle:
405 // Here we don't want to ship messages because these messages must be
406 // delivered by the cloned version of this one. They will be sent in the
407 // SendDisentangle().
408 return;
410 case eStateEntanglingForClose:
411 // We still want to deliver messages if we are closing. These messages
412 // are here from the previous eStateUnshippedEntangled state.
413 break;
415 case eStateEntangled:
416 // This port is up and running.
417 break;
419 case eStateDisentangling:
420 // If we are in the process to disentangle the port, we cannot dispatch
421 // messages. They will be sent to the cloned version of this port via
422 // SendDisentangle();
423 return;
425 case eStateDisentangled:
426 MOZ_CRASH("This cannot happen.");
427 // It cannot happen because Disentangle should take off all the pending
428 // messages.
429 break;
431 case eStateDisentangledForClose:
432 // If we are here is because the port has been closed. We can still
433 // process the pending messages.
434 break;
437 RefPtr<SharedMessageBody> data = mMessages.ElementAt(0);
438 mMessages.RemoveElementAt(0);
440 mPostMessageRunnable = new PostMessageRunnable(this, data);
441 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
444 void MessagePort::Close() {
445 mHasBeenTransferredOrClosed = true;
446 CloseInternal(true /* aSoftly */);
449 void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); }
451 void MessagePort::CloseInternal(bool aSoftly) {
452 // If we have some messages to send but we don't want a 'soft' close, we have
453 // to flush them now.
454 if (!aSoftly) {
455 mMessages.Clear();
458 // Let's inform the RefMessageBodyService that any our shared messages are
459 // now invalid.
460 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
462 if (mState == eStateUnshippedEntangled) {
463 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
465 // This avoids loops.
466 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
468 mState = eStateDisentangledForClose;
469 port->CloseInternal(aSoftly);
471 UpdateMustKeepAlive();
472 return;
475 // Not entangled yet, we have to wait.
476 if (mState == eStateEntangling) {
477 mState = eStateEntanglingForClose;
478 return;
481 // Not entangled but already cloned or closed
482 if (mState == eStateEntanglingForDisentangle ||
483 mState == eStateEntanglingForClose) {
484 return;
487 // Maybe we were already closing the port but softly. In this case we call
488 // UpdateMustKeepAlive() to consider the empty pending message queue.
489 if (mState == eStateDisentangledForClose && !aSoftly) {
490 UpdateMustKeepAlive();
491 return;
494 if (mState > eStateEntangled) {
495 return;
498 // We don't care about stopping the sending of messages because from now all
499 // the incoming messages will be ignored.
500 mState = eStateDisentangledForClose;
502 MOZ_ASSERT(mActor);
504 mActor->SendClose();
505 mActor->SetPort(nullptr);
506 mActor = nullptr;
508 UpdateMustKeepAlive();
511 EventHandlerNonNull* MessagePort::GetOnmessage() {
512 return GetEventHandler(nsGkAtoms::onmessage);
515 void MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) {
516 SetEventHandler(nsGkAtoms::onmessage, aCallback);
518 // When using onmessage, the call to start() is implied.
519 Start();
522 // This method is called when the PMessagePortChild actor is entangled to
523 // another actor. It receives a list of messages to be dispatch. It can be that
524 // we were waiting for this entangling step in order to disentangle the port or
525 // to close it.
526 void MessagePort::Entangled(nsTArray<MessageData>& aMessages) {
527 MOZ_ASSERT(mState == eStateEntangling ||
528 mState == eStateEntanglingForDisentangle ||
529 mState == eStateEntanglingForClose);
531 State oldState = mState;
532 mState = eStateEntangled;
534 // If we have pending messages, these have to be sent.
535 if (!mMessagesForTheOtherPort.IsEmpty()) {
537 nsTArray<MessageData> messages;
538 SharedMessageBody::FromSharedToMessagesChild(
539 mActor->Manager(), mMessagesForTheOtherPort, messages);
540 mActor->SendPostMessages(messages);
542 // Because `messages` borrow the underlying JSStructuredCloneData buffers,
543 // only clear after `messages` have gone out of scope.
544 mMessagesForTheOtherPort.Clear();
547 // We must convert the messages into SharedMessageBodys to avoid leaks.
548 FallibleTArray<RefPtr<SharedMessageBody>> data;
549 if (NS_WARN_IF(
550 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
551 DispatchError();
552 return;
555 // If the next step is to close the port, we do it ignoring the received
556 // messages.
557 if (oldState == eStateEntanglingForClose) {
558 CloseForced();
559 return;
562 mMessages.AppendElements(data);
564 // We were waiting for the entangling callback in order to disentangle this
565 // port immediately after.
566 if (oldState == eStateEntanglingForDisentangle) {
567 StartDisentangling();
568 return;
571 Dispatch();
574 void MessagePort::StartDisentangling() {
575 MOZ_ASSERT(mActor);
576 MOZ_ASSERT(mState == eStateEntangled);
578 mState = eStateDisentangling;
580 // Sending this message we communicate to the parent actor that we don't want
581 // to receive any new messages. It is possible that a message has been
582 // already sent but not received yet. So we have to collect all of them and
583 // we send them in the SendDispatch() request.
584 mActor->SendStopSendingData();
587 void MessagePort::MessagesReceived(nsTArray<MessageData>& aMessages) {
588 MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling ||
589 // This last step can happen only if Close() has been called
590 // manually. At this point SendClose() is sent but we can still
591 // receive something until the Closing request is processed.
592 mState == eStateDisentangledForClose);
593 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
595 RemoveDocFromBFCache();
597 FallibleTArray<RefPtr<SharedMessageBody>> data;
598 if (NS_WARN_IF(
599 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
600 DispatchError();
601 return;
604 mMessages.AppendElements(data);
606 if (mState == eStateEntangled) {
607 Dispatch();
611 void MessagePort::StopSendingDataConfirmed() {
612 MOZ_ASSERT(mState == eStateDisentangling);
613 MOZ_ASSERT(mActor);
615 Disentangle();
618 void MessagePort::Disentangle() {
619 MOZ_ASSERT(mState == eStateDisentangling);
620 MOZ_ASSERT(mActor);
622 mState = eStateDisentangled;
625 nsTArray<MessageData> messages;
626 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), mMessages,
627 messages);
628 mActor->SendDisentangle(messages);
631 // Let's inform the RefMessageBodyService that any our shared messages are
632 // now invalid.
633 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
635 // Only clear mMessages after the MessageData instances have gone out of scope
636 // because they borrow mMessages' underlying JSStructuredCloneDatas.
637 mMessages.Clear();
639 mActor->SetPort(nullptr);
640 mActor = nullptr;
642 UpdateMustKeepAlive();
645 void MessagePort::CloneAndDisentangle(UniqueMessagePortId& aIdentifier) {
646 MOZ_ASSERT(mIdentifier);
647 MOZ_ASSERT(!mHasBeenTransferredOrClosed);
649 mHasBeenTransferredOrClosed = true;
651 // We can clone a port that has already been transfered. In this case, on the
652 // otherside will have a neutered port. Here we set neutered to true so that
653 // we are safe in case a early return.
654 aIdentifier.neutered() = true;
656 if (mState > eStateEntangled) {
657 return;
660 // We already have a 'next step'. We have to consider this port as already
661 // cloned/closed/disentangled.
662 if (mState == eStateEntanglingForDisentangle ||
663 mState == eStateEntanglingForClose) {
664 return;
667 aIdentifier.uuid() = mIdentifier->uuid();
668 aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
669 aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
670 aIdentifier.neutered() = false;
672 // We have to entangle first.
673 if (mState == eStateUnshippedEntangled) {
674 MOZ_ASSERT(mUnshippedEntangledPort);
675 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
677 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
679 // Disconnect the entangled port and connect it to PBackground.
680 if (!port->ConnectToPBackground()) {
681 // We are probably shutting down. We cannot proceed.
682 mState = eStateDisentangled;
683 UpdateMustKeepAlive();
684 return;
687 // In this case, we don't need to be connected to the PBackground service.
688 if (mMessages.IsEmpty()) {
689 aIdentifier.sequenceId() = mIdentifier->sequenceId();
691 mState = eStateDisentangled;
692 UpdateMustKeepAlive();
693 return;
696 // Register this component to PBackground.
697 if (!ConnectToPBackground()) {
698 // We are probably shutting down. We cannot proceed.
699 return;
702 mState = eStateEntanglingForDisentangle;
703 return;
706 // Not entangled yet, we have to wait.
707 if (mState == eStateEntangling) {
708 mState = eStateEntanglingForDisentangle;
709 return;
712 MOZ_ASSERT(mState == eStateEntangled);
713 StartDisentangling();
716 void MessagePort::Closed() {
717 if (mState >= eStateDisentangled) {
718 return;
721 mState = eStateDisentangledForClose;
723 if (mActor) {
724 mActor->SetPort(nullptr);
725 mActor = nullptr;
728 UpdateMustKeepAlive();
731 bool MessagePort::ConnectToPBackground() {
732 RefPtr<MessagePort> self = this;
733 auto raii = MakeScopeExit([self] {
734 self->mState = eStateDisentangled;
735 self->UpdateMustKeepAlive();
738 mozilla::ipc::PBackgroundChild* actorChild =
739 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
740 if (NS_WARN_IF(!actorChild)) {
741 return false;
744 PMessagePortChild* actor = actorChild->SendPMessagePortConstructor(
745 mIdentifier->uuid(), mIdentifier->destinationUuid(),
746 mIdentifier->sequenceId());
747 if (NS_WARN_IF(!actor)) {
748 return false;
751 mActor = static_cast<MessagePortChild*>(actor);
752 MOZ_ASSERT(mActor);
754 mActor->SetPort(this);
755 mState = eStateEntangling;
757 raii.release();
758 return true;
761 void MessagePort::UpdateMustKeepAlive() {
762 if (mState >= eStateDisentangled && mMessages.IsEmpty() && mIsKeptAlive) {
763 mIsKeptAlive = false;
765 // The DTOR of this WorkerRef will release the worker for us.
766 mWorkerRef = nullptr;
768 Release();
769 return;
772 if (mState < eStateDisentangled && !mIsKeptAlive) {
773 mIsKeptAlive = true;
774 AddRef();
778 void MessagePort::DisconnectFromOwner() {
779 CloseForced();
780 DOMEventTargetHelper::DisconnectFromOwner();
783 void MessagePort::RemoveDocFromBFCache() {
784 if (!NS_IsMainThread()) {
785 return;
788 if (nsPIDOMWindowInner* window = GetOwner()) {
789 window->RemoveFromBFCacheSync();
793 /* static */
794 void MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier) {
795 mozilla::ipc::PBackgroundChild* actorChild =
796 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
797 if (NS_WARN_IF(!actorChild)) {
798 MOZ_CRASH("Failed to create a PBackgroundChild actor!");
801 Unused << actorChild->SendMessagePortForceClose(aIdentifier.uuid(),
802 aIdentifier.destinationUuid(),
803 aIdentifier.sequenceId());
806 void MessagePort::DispatchError() {
807 nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
809 AutoJSAPI jsapi;
810 if (!globalObject || !jsapi.Init(globalObject)) {
811 NS_WARNING("Failed to initialize AutoJSAPI object.");
812 return;
815 RootedDictionary<MessageEventInit> init(jsapi.cx());
816 init.mBubbles = false;
817 init.mCancelable = false;
819 RefPtr<Event> event =
820 MessageEvent::Constructor(this, u"messageerror"_ns, init);
821 event->SetTrusted(true);
823 DispatchEvent(*event);
826 } // namespace mozilla::dom