Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / dom / messagechannel / MessagePort.cpp
blob100b5f56a08a3a16e7c334d34279d4f8f53a1464
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 RefPtr<MessageEvent> event =
123 new MessageEvent(mPort->GetOwnerWindow(), nullptr, nullptr);
125 Sequence<OwningNonNull<MessagePort>> ports;
126 if (!mData->TakeTransferredPortsAsSequence(ports)) {
127 mPort->DispatchError();
128 return;
131 event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
132 Cancelable::eNo, value, u""_ns, u""_ns, nullptr,
133 ports);
134 event->SetTrusted(true);
136 mPort->DispatchEvent(*event);
139 private:
140 ~PostMessageRunnable() = default;
142 RefPtr<MessagePort> mPort;
143 RefPtr<SharedMessageBody> mData;
146 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
148 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
149 DOMEventTargetHelper)
150 if (tmp->mPostMessageRunnable) {
151 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
154 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
156 tmp->CloseForced();
157 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
160 DOMEventTargetHelper)
161 if (tmp->mPostMessageRunnable) {
162 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
168 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePort)
169 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
171 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
172 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
174 MessagePort::MessagePort(nsIGlobalObject* aGlobal, State aState)
175 : DOMEventTargetHelper(aGlobal),
176 mRefMessageBodyService(RefMessageBodyService::GetOrCreate()),
177 mState(aState),
178 mMessageQueueEnabled(false),
179 mIsKeptAlive(false),
180 mHasBeenTransferredOrClosed(false) {
181 MOZ_ASSERT(aGlobal);
183 mIdentifier = MakeUnique<MessagePortIdentifier>();
184 mIdentifier->neutered() = true;
185 mIdentifier->sequenceId() = 0;
188 MessagePort::~MessagePort() {
189 CloseForced();
190 MOZ_ASSERT(!mActor);
191 if (mActor) {
192 mActor->SetPort(nullptr);
193 mActor = nullptr;
195 MOZ_ASSERT(!mWorkerRef);
198 /* static */
199 already_AddRefed<MessagePort> MessagePort::Create(nsIGlobalObject* aGlobal,
200 const nsID& aUUID,
201 const nsID& aDestinationUUID,
202 ErrorResult& aRv) {
203 MOZ_ASSERT(aGlobal);
205 RefPtr<MessagePort> mp =
206 new MessagePort(aGlobal, eStateInitializingUnshippedEntangled);
207 mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
208 false /* Neutered */, aRv);
209 return mp.forget();
212 /* static */
213 already_AddRefed<MessagePort> MessagePort::Create(
214 nsIGlobalObject* aGlobal, UniqueMessagePortId& aIdentifier,
215 ErrorResult& aRv) {
216 MOZ_ASSERT(aGlobal);
218 RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateEntangling);
219 mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
220 aIdentifier.sequenceId(), aIdentifier.neutered(), aRv);
221 aIdentifier.neutered() = true;
222 return mp.forget();
225 void MessagePort::UnshippedEntangle(RefPtr<MessagePort>& aEntangledPort) {
226 MOZ_DIAGNOSTIC_ASSERT(aEntangledPort);
227 MOZ_DIAGNOSTIC_ASSERT(!mUnshippedEntangledPort);
229 if (mState == eStateInitializingUnshippedEntangled) {
230 mUnshippedEntangledPort = aEntangledPort;
231 mState = eStateUnshippedEntangled;
232 } else {
233 MOZ_ASSERT_UNREACHABLE("Should not have been called.");
237 void MessagePort::Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
238 uint32_t aSequenceID, bool aNeutered,
239 ErrorResult& aRv) {
240 MOZ_ASSERT(mIdentifier);
241 mIdentifier->uuid() = aUUID;
242 mIdentifier->destinationUuid() = aDestinationUUID;
243 mIdentifier->sequenceId() = aSequenceID;
245 if (aNeutered) {
246 // If this port is neutered we don't want to keep it alive artificially nor
247 // we want to add listeners or WorkerRefs.
248 mState = eStateDisentangled;
249 return;
252 if (mState == eStateEntangling) {
253 if (!ConnectToPBackground()) {
254 aRv.Throw(NS_ERROR_FAILURE);
255 return;
257 } else {
258 MOZ_ASSERT(mState == eStateInitializingUnshippedEntangled);
261 // The port has to keep itself alive until it's entangled.
262 UpdateMustKeepAlive();
264 if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
265 RefPtr<MessagePort> self = this;
267 // When the callback is executed, we cannot process messages anymore because
268 // we cannot dispatch new runnables. Let's force a Close().
269 RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
270 workerPrivate, "MessagePort", [self]() { self->CloseForced(); });
271 if (NS_WARN_IF(!strongWorkerRef)) {
272 // The worker is shutting down.
273 CloseForced();
274 aRv.Throw(NS_ERROR_FAILURE);
275 return;
278 MOZ_ASSERT(!mWorkerRef);
279 mWorkerRef = std::move(strongWorkerRef);
283 JSObject* MessagePort::WrapObject(JSContext* aCx,
284 JS::Handle<JSObject*> aGivenProto) {
285 return MessagePort_Binding::Wrap(aCx, this, aGivenProto);
288 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
289 const Sequence<JSObject*>& aTransferable,
290 ErrorResult& aRv) {
291 // We *must* clone the data here, or the JS::Value could be modified
292 // by script
294 // Here we want to check if the transerable object list contains
295 // this port.
296 for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
297 JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
298 if (!object) {
299 continue;
302 MessagePort* port = nullptr;
303 nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
304 if (NS_SUCCEEDED(rv) && port == this) {
305 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
306 return;
310 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
312 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
313 &transferable);
314 if (NS_WARN_IF(aRv.Failed())) {
315 return;
318 Maybe<nsID> agentClusterId;
319 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
320 if (global) {
321 agentClusterId = global->GetAgentClusterId();
324 RefPtr<SharedMessageBody> data = new SharedMessageBody(
325 StructuredCloneHolder::TransferringSupported, agentClusterId);
327 data->Write(aCx, aMessage, transferable, mIdentifier->uuid(),
328 mRefMessageBodyService, aRv);
330 if (NS_WARN_IF(aRv.Failed())) {
331 return;
334 // This message has to be ignored.
335 if (mState > eStateEntangled) {
336 return;
339 if (mState == eStateInitializingUnshippedEntangled) {
340 MOZ_ASSERT_UNREACHABLE(
341 "Should be eStateUnshippedEntangled or eStateDisentangled by now.");
342 return;
345 // If we are unshipped we are connected to the other port on the same thread.
346 if (mState == eStateUnshippedEntangled) {
347 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
348 mUnshippedEntangledPort->mMessages.AppendElement(data);
349 mUnshippedEntangledPort->Dispatch();
350 return;
353 // Not entangled yet, but already closed/disentangled.
354 if (mState == eStateEntanglingForDisentangle ||
355 mState == eStateEntanglingForClose) {
356 return;
359 RemoveDocFromBFCache();
361 // Not entangled yet.
362 if (mState == eStateEntangling) {
363 mMessagesForTheOtherPort.AppendElement(data);
364 return;
367 MOZ_ASSERT(mActor);
368 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
370 AutoTArray<RefPtr<SharedMessageBody>, 1> array;
371 array.AppendElement(data);
373 AutoTArray<MessageData, 1> messages;
374 // note: `messages` will borrow the underlying buffer, but this is okay
375 // because reverse destruction order means `messages` will be destroyed prior
376 // to `array`/`data`.
377 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), array,
378 messages);
379 mActor->SendPostMessages(messages);
382 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
383 const StructuredSerializeOptions& aOptions,
384 ErrorResult& aRv) {
385 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
388 void MessagePort::Start() {
389 if (mMessageQueueEnabled) {
390 return;
393 mMessageQueueEnabled = true;
394 Dispatch();
397 void MessagePort::Dispatch() {
398 if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
399 return;
402 switch (mState) {
403 case eStateInitializingUnshippedEntangled:
404 MOZ_ASSERT_UNREACHABLE(
405 "Should be eStateUnshippedEntangled or eStateDisentangled by now.");
406 break;
408 case eStateUnshippedEntangled:
409 // Everything is fine here. We have messages because the other
410 // port populates our queue directly.
411 break;
413 case eStateEntangling:
414 // Everything is fine here as well. We have messages because the other
415 // port populated our queue directly when we were in the
416 // eStateUnshippedEntangled state.
417 break;
419 case eStateEntanglingForDisentangle:
420 // Here we don't want to ship messages because these messages must be
421 // delivered by the cloned version of this one. They will be sent in the
422 // SendDisentangle().
423 return;
425 case eStateEntanglingForClose:
426 // We still want to deliver messages if we are closing. These messages
427 // are here from the previous eStateUnshippedEntangled state.
428 break;
430 case eStateEntangled:
431 // This port is up and running.
432 break;
434 case eStateDisentangling:
435 // If we are in the process to disentangle the port, we cannot dispatch
436 // messages. They will be sent to the cloned version of this port via
437 // SendDisentangle();
438 return;
440 case eStateDisentangled:
441 MOZ_CRASH("This cannot happen.");
442 // It cannot happen because Disentangle should take off all the pending
443 // messages.
444 break;
446 case eStateDisentangledForClose:
447 // If we are here is because the port has been closed. We can still
448 // process the pending messages.
449 break;
452 RefPtr<SharedMessageBody> data = mMessages.ElementAt(0);
453 mMessages.RemoveElementAt(0);
455 mPostMessageRunnable = new PostMessageRunnable(this, data);
456 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
459 void MessagePort::Close() {
460 mHasBeenTransferredOrClosed = true;
461 CloseInternal(true /* aSoftly */);
464 void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); }
466 void MessagePort::CloseInternal(bool aSoftly) {
467 // If we have some messages to send but we don't want a 'soft' close, we have
468 // to flush them now.
469 if (!aSoftly) {
470 mMessages.Clear();
473 // Let's inform the RefMessageBodyService that any our shared messages are
474 // now invalid.
475 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
477 if (mState == eStateInitializingUnshippedEntangled) {
478 // We can end up here if we failed to create our WorkerRef. Our Create
479 // method will end up returning an error and MessageChannel will not bother
480 // creating our counterpart port or calling UnshippedEntangle (and we
481 // assert on this).
482 mState = eStateDisentangledForClose;
484 UpdateMustKeepAlive();
485 return;
488 if (mState == eStateUnshippedEntangled) {
489 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
491 // This avoids loops.
492 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
494 mState = eStateDisentangledForClose;
495 port->CloseInternal(aSoftly);
497 UpdateMustKeepAlive();
498 return;
501 // Not entangled yet, we have to wait.
502 if (mState == eStateEntangling) {
503 mState = eStateEntanglingForClose;
504 return;
507 // Not entangled but already cloned or closed
508 if (mState == eStateEntanglingForDisentangle ||
509 mState == eStateEntanglingForClose) {
510 return;
513 // Maybe we were already closing the port but softly. In this case we call
514 // UpdateMustKeepAlive() to consider the empty pending message queue.
515 if (mState == eStateDisentangledForClose && !aSoftly) {
516 UpdateMustKeepAlive();
517 return;
520 if (mState > eStateEntangled) {
521 return;
524 // We don't care about stopping the sending of messages because from now all
525 // the incoming messages will be ignored.
526 mState = eStateDisentangledForClose;
528 MOZ_ASSERT(mActor);
530 mActor->SendClose();
531 mActor->SetPort(nullptr);
532 mActor = nullptr;
534 UpdateMustKeepAlive();
537 EventHandlerNonNull* MessagePort::GetOnmessage() {
538 return GetEventHandler(nsGkAtoms::onmessage);
541 void MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) {
542 SetEventHandler(nsGkAtoms::onmessage, aCallback);
544 // When using onmessage, the call to start() is implied.
545 Start();
548 // This method is called when the PMessagePortChild actor is entangled to
549 // another actor. It receives a list of messages to be dispatch. It can be that
550 // we were waiting for this entangling step in order to disentangle the port or
551 // to close it.
552 void MessagePort::Entangled(nsTArray<MessageData>& aMessages) {
553 MOZ_ASSERT(mState == eStateEntangling ||
554 mState == eStateEntanglingForDisentangle ||
555 mState == eStateEntanglingForClose);
557 State oldState = mState;
558 mState = eStateEntangled;
560 // If we have pending messages, these have to be sent.
561 if (!mMessagesForTheOtherPort.IsEmpty()) {
563 nsTArray<MessageData> messages;
564 SharedMessageBody::FromSharedToMessagesChild(
565 mActor->Manager(), mMessagesForTheOtherPort, messages);
566 mActor->SendPostMessages(messages);
568 // Because `messages` borrow the underlying JSStructuredCloneData buffers,
569 // only clear after `messages` have gone out of scope.
570 mMessagesForTheOtherPort.Clear();
573 // We must convert the messages into SharedMessageBodys to avoid leaks.
574 FallibleTArray<RefPtr<SharedMessageBody>> data;
575 if (NS_WARN_IF(
576 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
577 DispatchError();
578 return;
581 // If the next step is to close the port, we do it ignoring the received
582 // messages.
583 if (oldState == eStateEntanglingForClose) {
584 CloseForced();
585 return;
588 mMessages.AppendElements(data);
590 // We were waiting for the entangling callback in order to disentangle this
591 // port immediately after.
592 if (oldState == eStateEntanglingForDisentangle) {
593 StartDisentangling();
594 return;
597 Dispatch();
600 void MessagePort::StartDisentangling() {
601 MOZ_ASSERT(mActor);
602 MOZ_ASSERT(mState == eStateEntangled);
604 mState = eStateDisentangling;
606 // Sending this message we communicate to the parent actor that we don't want
607 // to receive any new messages. It is possible that a message has been
608 // already sent but not received yet. So we have to collect all of them and
609 // we send them in the SendDispatch() request.
610 mActor->SendStopSendingData();
613 void MessagePort::MessagesReceived(nsTArray<MessageData>& aMessages) {
614 MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling ||
615 // This last step can happen only if Close() has been called
616 // manually. At this point SendClose() is sent but we can still
617 // receive something until the Closing request is processed.
618 mState == eStateDisentangledForClose);
619 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
621 RemoveDocFromBFCache();
623 FallibleTArray<RefPtr<SharedMessageBody>> data;
624 if (NS_WARN_IF(
625 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
626 DispatchError();
627 return;
630 mMessages.AppendElements(data);
632 if (mState == eStateEntangled) {
633 Dispatch();
637 void MessagePort::StopSendingDataConfirmed() {
638 MOZ_ASSERT(mState == eStateDisentangling);
639 MOZ_ASSERT(mActor);
641 Disentangle();
644 void MessagePort::Disentangle() {
645 MOZ_ASSERT(mState == eStateDisentangling);
646 MOZ_ASSERT(mActor);
648 mState = eStateDisentangled;
651 nsTArray<MessageData> messages;
652 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), mMessages,
653 messages);
654 mActor->SendDisentangle(messages);
657 // Let's inform the RefMessageBodyService that any our shared messages are
658 // now invalid.
659 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
661 // Only clear mMessages after the MessageData instances have gone out of scope
662 // because they borrow mMessages' underlying JSStructuredCloneDatas.
663 mMessages.Clear();
665 mActor->SetPort(nullptr);
666 mActor = nullptr;
668 UpdateMustKeepAlive();
671 void MessagePort::CloneAndDisentangle(UniqueMessagePortId& aIdentifier) {
672 MOZ_ASSERT(mIdentifier);
673 MOZ_ASSERT(!mHasBeenTransferredOrClosed);
675 mHasBeenTransferredOrClosed = true;
677 // We can clone a port that has already been transfered. In this case, on the
678 // otherside will have a neutered port. Here we set neutered to true so that
679 // we are safe in case a early return.
680 aIdentifier.neutered() = true;
682 if (mState > eStateEntangled) {
683 return;
686 // We already have a 'next step'. We have to consider this port as already
687 // cloned/closed/disentangled.
688 if (mState == eStateEntanglingForDisentangle ||
689 mState == eStateEntanglingForClose) {
690 return;
693 aIdentifier.uuid() = mIdentifier->uuid();
694 aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
695 aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
696 aIdentifier.neutered() = false;
698 // We have to entangle first.
699 if (mState == eStateUnshippedEntangled) {
700 MOZ_ASSERT(mUnshippedEntangledPort);
701 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
703 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
705 // Disconnect the entangled port and connect it to PBackground.
706 if (!port->ConnectToPBackground()) {
707 // We are probably shutting down. We cannot proceed.
708 mState = eStateDisentangled;
709 UpdateMustKeepAlive();
710 return;
713 // In this case, we don't need to be connected to the PBackground service.
714 if (mMessages.IsEmpty()) {
715 aIdentifier.sequenceId() = mIdentifier->sequenceId();
717 mState = eStateDisentangled;
718 UpdateMustKeepAlive();
719 return;
722 // Register this component to PBackground.
723 if (!ConnectToPBackground()) {
724 // We are probably shutting down. We cannot proceed.
725 return;
728 mState = eStateEntanglingForDisentangle;
729 return;
732 // Not entangled yet, we have to wait.
733 if (mState == eStateEntangling) {
734 mState = eStateEntanglingForDisentangle;
735 return;
738 MOZ_ASSERT(mState == eStateEntangled);
739 StartDisentangling();
742 void MessagePort::Closed() {
743 if (mState >= eStateDisentangled) {
744 return;
747 mState = eStateDisentangledForClose;
749 if (mActor) {
750 mActor->SetPort(nullptr);
751 mActor = nullptr;
754 UpdateMustKeepAlive();
757 bool MessagePort::ConnectToPBackground() {
758 RefPtr<MessagePort> self = this;
759 auto raii = MakeScopeExit([self] {
760 self->mState = eStateDisentangled;
761 self->UpdateMustKeepAlive();
764 mozilla::ipc::PBackgroundChild* actorChild =
765 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
766 if (NS_WARN_IF(!actorChild)) {
767 return false;
770 PMessagePortChild* actor = actorChild->SendPMessagePortConstructor(
771 mIdentifier->uuid(), mIdentifier->destinationUuid(),
772 mIdentifier->sequenceId());
773 if (NS_WARN_IF(!actor)) {
774 return false;
777 mActor = static_cast<MessagePortChild*>(actor);
778 MOZ_ASSERT(mActor);
780 mActor->SetPort(this);
781 mState = eStateEntangling;
783 raii.release();
784 return true;
787 void MessagePort::UpdateMustKeepAlive() {
788 if (mState >= eStateDisentangled && mMessages.IsEmpty() && mIsKeptAlive) {
789 mIsKeptAlive = false;
791 // The DTOR of this WorkerRef will release the worker for us.
792 mWorkerRef = nullptr;
794 Release();
795 return;
798 if (mState < eStateDisentangled && !mIsKeptAlive) {
799 mIsKeptAlive = true;
800 AddRef();
804 void MessagePort::DisconnectFromOwner() {
805 CloseForced();
806 DOMEventTargetHelper::DisconnectFromOwner();
809 void MessagePort::RemoveDocFromBFCache() {
810 if (!NS_IsMainThread()) {
811 return;
814 if (nsPIDOMWindowInner* window = GetOwnerWindow()) {
815 window->RemoveFromBFCacheSync();
819 /* static */
820 void MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier) {
821 mozilla::ipc::PBackgroundChild* actorChild =
822 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
823 if (NS_WARN_IF(!actorChild)) {
824 MOZ_CRASH("Failed to create a PBackgroundChild actor!");
827 Unused << actorChild->SendMessagePortForceClose(aIdentifier.uuid(),
828 aIdentifier.destinationUuid(),
829 aIdentifier.sequenceId());
832 void MessagePort::DispatchError() {
833 nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
835 AutoJSAPI jsapi;
836 if (!globalObject || !jsapi.Init(globalObject)) {
837 NS_WARNING("Failed to initialize AutoJSAPI object.");
838 return;
841 RootedDictionary<MessageEventInit> init(jsapi.cx());
842 init.mBubbles = false;
843 init.mCancelable = false;
845 RefPtr<Event> event =
846 MessageEvent::Constructor(this, u"messageerror"_ns, init);
847 event->SetTrusted(true);
849 DispatchEvent(*event);
852 } // namespace mozilla::dom