Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / dom / messagechannel / MessagePort.cpp
blob5c073114277d2238c6b514df888f82fc953219ca
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 =
208 new MessagePort(aGlobal, eStateInitializingUnshippedEntangled);
209 mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
210 false /* Neutered */, aRv);
211 return mp.forget();
214 /* static */
215 already_AddRefed<MessagePort> MessagePort::Create(
216 nsIGlobalObject* aGlobal, UniqueMessagePortId& aIdentifier,
217 ErrorResult& aRv) {
218 MOZ_ASSERT(aGlobal);
220 RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateEntangling);
221 mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
222 aIdentifier.sequenceId(), aIdentifier.neutered(), aRv);
223 aIdentifier.neutered() = true;
224 return mp.forget();
227 void MessagePort::UnshippedEntangle(RefPtr<MessagePort>& aEntangledPort) {
228 MOZ_DIAGNOSTIC_ASSERT(aEntangledPort);
229 MOZ_DIAGNOSTIC_ASSERT(!mUnshippedEntangledPort);
231 if (mState == eStateInitializingUnshippedEntangled) {
232 mUnshippedEntangledPort = aEntangledPort;
233 mState = eStateUnshippedEntangled;
234 } else {
235 MOZ_ASSERT_UNREACHABLE("Should not have been called.");
239 void MessagePort::Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
240 uint32_t aSequenceID, bool aNeutered,
241 ErrorResult& aRv) {
242 MOZ_ASSERT(mIdentifier);
243 mIdentifier->uuid() = aUUID;
244 mIdentifier->destinationUuid() = aDestinationUUID;
245 mIdentifier->sequenceId() = aSequenceID;
247 if (aNeutered) {
248 // If this port is neutered we don't want to keep it alive artificially nor
249 // we want to add listeners or WorkerRefs.
250 mState = eStateDisentangled;
251 return;
254 if (mState == eStateEntangling) {
255 if (!ConnectToPBackground()) {
256 aRv.Throw(NS_ERROR_FAILURE);
257 return;
259 } else {
260 MOZ_ASSERT(mState == eStateInitializingUnshippedEntangled);
263 // The port has to keep itself alive until it's entangled.
264 UpdateMustKeepAlive();
266 if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
267 RefPtr<MessagePort> self = this;
269 // When the callback is executed, we cannot process messages anymore because
270 // we cannot dispatch new runnables. Let's force a Close().
271 RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
272 workerPrivate, "MessagePort", [self]() { self->CloseForced(); });
273 if (NS_WARN_IF(!strongWorkerRef)) {
274 // The worker is shutting down.
275 CloseForced();
276 aRv.Throw(NS_ERROR_FAILURE);
277 return;
280 MOZ_ASSERT(!mWorkerRef);
281 mWorkerRef = std::move(strongWorkerRef);
285 JSObject* MessagePort::WrapObject(JSContext* aCx,
286 JS::Handle<JSObject*> aGivenProto) {
287 return MessagePort_Binding::Wrap(aCx, this, aGivenProto);
290 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
291 const Sequence<JSObject*>& aTransferable,
292 ErrorResult& aRv) {
293 // We *must* clone the data here, or the JS::Value could be modified
294 // by script
296 // Here we want to check if the transerable object list contains
297 // this port.
298 for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
299 JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
300 if (!object) {
301 continue;
304 MessagePort* port = nullptr;
305 nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
306 if (NS_SUCCEEDED(rv) && port == this) {
307 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
308 return;
312 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
314 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
315 &transferable);
316 if (NS_WARN_IF(aRv.Failed())) {
317 return;
320 Maybe<nsID> agentClusterId;
321 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
322 if (global) {
323 agentClusterId = global->GetAgentClusterId();
326 RefPtr<SharedMessageBody> data = new SharedMessageBody(
327 StructuredCloneHolder::TransferringSupported, agentClusterId);
329 data->Write(aCx, aMessage, transferable, mIdentifier->uuid(),
330 mRefMessageBodyService, aRv);
332 if (NS_WARN_IF(aRv.Failed())) {
333 return;
336 // This message has to be ignored.
337 if (mState > eStateEntangled) {
338 return;
341 if (mState == eStateInitializingUnshippedEntangled) {
342 MOZ_ASSERT_UNREACHABLE(
343 "Should be eStateUnshippedEntangled or eStateDisentangled by now.");
344 return;
347 // If we are unshipped we are connected to the other port on the same thread.
348 if (mState == eStateUnshippedEntangled) {
349 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
350 mUnshippedEntangledPort->mMessages.AppendElement(data);
351 mUnshippedEntangledPort->Dispatch();
352 return;
355 // Not entangled yet, but already closed/disentangled.
356 if (mState == eStateEntanglingForDisentangle ||
357 mState == eStateEntanglingForClose) {
358 return;
361 RemoveDocFromBFCache();
363 // Not entangled yet.
364 if (mState == eStateEntangling) {
365 mMessagesForTheOtherPort.AppendElement(data);
366 return;
369 MOZ_ASSERT(mActor);
370 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
372 AutoTArray<RefPtr<SharedMessageBody>, 1> array;
373 array.AppendElement(data);
375 AutoTArray<MessageData, 1> messages;
376 // note: `messages` will borrow the underlying buffer, but this is okay
377 // because reverse destruction order means `messages` will be destroyed prior
378 // to `array`/`data`.
379 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), array,
380 messages);
381 mActor->SendPostMessages(messages);
384 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
385 const StructuredSerializeOptions& aOptions,
386 ErrorResult& aRv) {
387 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
390 void MessagePort::Start() {
391 if (mMessageQueueEnabled) {
392 return;
395 mMessageQueueEnabled = true;
396 Dispatch();
399 void MessagePort::Dispatch() {
400 if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
401 return;
404 switch (mState) {
405 case eStateInitializingUnshippedEntangled:
406 MOZ_ASSERT_UNREACHABLE(
407 "Should be eStateUnshippedEntangled or eStateDisentangled by now.");
408 break;
410 case eStateUnshippedEntangled:
411 // Everything is fine here. We have messages because the other
412 // port populates our queue directly.
413 break;
415 case eStateEntangling:
416 // Everything is fine here as well. We have messages because the other
417 // port populated our queue directly when we were in the
418 // eStateUnshippedEntangled state.
419 break;
421 case eStateEntanglingForDisentangle:
422 // Here we don't want to ship messages because these messages must be
423 // delivered by the cloned version of this one. They will be sent in the
424 // SendDisentangle().
425 return;
427 case eStateEntanglingForClose:
428 // We still want to deliver messages if we are closing. These messages
429 // are here from the previous eStateUnshippedEntangled state.
430 break;
432 case eStateEntangled:
433 // This port is up and running.
434 break;
436 case eStateDisentangling:
437 // If we are in the process to disentangle the port, we cannot dispatch
438 // messages. They will be sent to the cloned version of this port via
439 // SendDisentangle();
440 return;
442 case eStateDisentangled:
443 MOZ_CRASH("This cannot happen.");
444 // It cannot happen because Disentangle should take off all the pending
445 // messages.
446 break;
448 case eStateDisentangledForClose:
449 // If we are here is because the port has been closed. We can still
450 // process the pending messages.
451 break;
454 RefPtr<SharedMessageBody> data = mMessages.ElementAt(0);
455 mMessages.RemoveElementAt(0);
457 mPostMessageRunnable = new PostMessageRunnable(this, data);
458 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
461 void MessagePort::Close() {
462 mHasBeenTransferredOrClosed = true;
463 CloseInternal(true /* aSoftly */);
466 void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); }
468 void MessagePort::CloseInternal(bool aSoftly) {
469 // If we have some messages to send but we don't want a 'soft' close, we have
470 // to flush them now.
471 if (!aSoftly) {
472 mMessages.Clear();
475 // Let's inform the RefMessageBodyService that any our shared messages are
476 // now invalid.
477 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
479 if (mState == eStateInitializingUnshippedEntangled) {
480 // We can end up here if we failed to create our WorkerRef. Our Create
481 // method will end up returning an error and MessageChannel will not bother
482 // creating our counterpart port or calling UnshippedEntangle (and we
483 // assert on this).
484 mState = eStateDisentangledForClose;
486 UpdateMustKeepAlive();
487 return;
490 if (mState == eStateUnshippedEntangled) {
491 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
493 // This avoids loops.
494 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
496 mState = eStateDisentangledForClose;
497 port->CloseInternal(aSoftly);
499 UpdateMustKeepAlive();
500 return;
503 // Not entangled yet, we have to wait.
504 if (mState == eStateEntangling) {
505 mState = eStateEntanglingForClose;
506 return;
509 // Not entangled but already cloned or closed
510 if (mState == eStateEntanglingForDisentangle ||
511 mState == eStateEntanglingForClose) {
512 return;
515 // Maybe we were already closing the port but softly. In this case we call
516 // UpdateMustKeepAlive() to consider the empty pending message queue.
517 if (mState == eStateDisentangledForClose && !aSoftly) {
518 UpdateMustKeepAlive();
519 return;
522 if (mState > eStateEntangled) {
523 return;
526 // We don't care about stopping the sending of messages because from now all
527 // the incoming messages will be ignored.
528 mState = eStateDisentangledForClose;
530 MOZ_ASSERT(mActor);
532 mActor->SendClose();
533 mActor->SetPort(nullptr);
534 mActor = nullptr;
536 UpdateMustKeepAlive();
539 EventHandlerNonNull* MessagePort::GetOnmessage() {
540 return GetEventHandler(nsGkAtoms::onmessage);
543 void MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) {
544 SetEventHandler(nsGkAtoms::onmessage, aCallback);
546 // When using onmessage, the call to start() is implied.
547 Start();
550 // This method is called when the PMessagePortChild actor is entangled to
551 // another actor. It receives a list of messages to be dispatch. It can be that
552 // we were waiting for this entangling step in order to disentangle the port or
553 // to close it.
554 void MessagePort::Entangled(nsTArray<MessageData>& aMessages) {
555 MOZ_ASSERT(mState == eStateEntangling ||
556 mState == eStateEntanglingForDisentangle ||
557 mState == eStateEntanglingForClose);
559 State oldState = mState;
560 mState = eStateEntangled;
562 // If we have pending messages, these have to be sent.
563 if (!mMessagesForTheOtherPort.IsEmpty()) {
565 nsTArray<MessageData> messages;
566 SharedMessageBody::FromSharedToMessagesChild(
567 mActor->Manager(), mMessagesForTheOtherPort, messages);
568 mActor->SendPostMessages(messages);
570 // Because `messages` borrow the underlying JSStructuredCloneData buffers,
571 // only clear after `messages` have gone out of scope.
572 mMessagesForTheOtherPort.Clear();
575 // We must convert the messages into SharedMessageBodys to avoid leaks.
576 FallibleTArray<RefPtr<SharedMessageBody>> data;
577 if (NS_WARN_IF(
578 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
579 DispatchError();
580 return;
583 // If the next step is to close the port, we do it ignoring the received
584 // messages.
585 if (oldState == eStateEntanglingForClose) {
586 CloseForced();
587 return;
590 mMessages.AppendElements(data);
592 // We were waiting for the entangling callback in order to disentangle this
593 // port immediately after.
594 if (oldState == eStateEntanglingForDisentangle) {
595 StartDisentangling();
596 return;
599 Dispatch();
602 void MessagePort::StartDisentangling() {
603 MOZ_ASSERT(mActor);
604 MOZ_ASSERT(mState == eStateEntangled);
606 mState = eStateDisentangling;
608 // Sending this message we communicate to the parent actor that we don't want
609 // to receive any new messages. It is possible that a message has been
610 // already sent but not received yet. So we have to collect all of them and
611 // we send them in the SendDispatch() request.
612 mActor->SendStopSendingData();
615 void MessagePort::MessagesReceived(nsTArray<MessageData>& aMessages) {
616 MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling ||
617 // This last step can happen only if Close() has been called
618 // manually. At this point SendClose() is sent but we can still
619 // receive something until the Closing request is processed.
620 mState == eStateDisentangledForClose);
621 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
623 RemoveDocFromBFCache();
625 FallibleTArray<RefPtr<SharedMessageBody>> data;
626 if (NS_WARN_IF(
627 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
628 DispatchError();
629 return;
632 mMessages.AppendElements(data);
634 if (mState == eStateEntangled) {
635 Dispatch();
639 void MessagePort::StopSendingDataConfirmed() {
640 MOZ_ASSERT(mState == eStateDisentangling);
641 MOZ_ASSERT(mActor);
643 Disentangle();
646 void MessagePort::Disentangle() {
647 MOZ_ASSERT(mState == eStateDisentangling);
648 MOZ_ASSERT(mActor);
650 mState = eStateDisentangled;
653 nsTArray<MessageData> messages;
654 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), mMessages,
655 messages);
656 mActor->SendDisentangle(messages);
659 // Let's inform the RefMessageBodyService that any our shared messages are
660 // now invalid.
661 mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
663 // Only clear mMessages after the MessageData instances have gone out of scope
664 // because they borrow mMessages' underlying JSStructuredCloneDatas.
665 mMessages.Clear();
667 mActor->SetPort(nullptr);
668 mActor = nullptr;
670 UpdateMustKeepAlive();
673 void MessagePort::CloneAndDisentangle(UniqueMessagePortId& aIdentifier) {
674 MOZ_ASSERT(mIdentifier);
675 MOZ_ASSERT(!mHasBeenTransferredOrClosed);
677 mHasBeenTransferredOrClosed = true;
679 // We can clone a port that has already been transfered. In this case, on the
680 // otherside will have a neutered port. Here we set neutered to true so that
681 // we are safe in case a early return.
682 aIdentifier.neutered() = true;
684 if (mState > eStateEntangled) {
685 return;
688 // We already have a 'next step'. We have to consider this port as already
689 // cloned/closed/disentangled.
690 if (mState == eStateEntanglingForDisentangle ||
691 mState == eStateEntanglingForClose) {
692 return;
695 aIdentifier.uuid() = mIdentifier->uuid();
696 aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
697 aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
698 aIdentifier.neutered() = false;
700 // We have to entangle first.
701 if (mState == eStateUnshippedEntangled) {
702 MOZ_ASSERT(mUnshippedEntangledPort);
703 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
705 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
707 // Disconnect the entangled port and connect it to PBackground.
708 if (!port->ConnectToPBackground()) {
709 // We are probably shutting down. We cannot proceed.
710 mState = eStateDisentangled;
711 UpdateMustKeepAlive();
712 return;
715 // In this case, we don't need to be connected to the PBackground service.
716 if (mMessages.IsEmpty()) {
717 aIdentifier.sequenceId() = mIdentifier->sequenceId();
719 mState = eStateDisentangled;
720 UpdateMustKeepAlive();
721 return;
724 // Register this component to PBackground.
725 if (!ConnectToPBackground()) {
726 // We are probably shutting down. We cannot proceed.
727 return;
730 mState = eStateEntanglingForDisentangle;
731 return;
734 // Not entangled yet, we have to wait.
735 if (mState == eStateEntangling) {
736 mState = eStateEntanglingForDisentangle;
737 return;
740 MOZ_ASSERT(mState == eStateEntangled);
741 StartDisentangling();
744 void MessagePort::Closed() {
745 if (mState >= eStateDisentangled) {
746 return;
749 mState = eStateDisentangledForClose;
751 if (mActor) {
752 mActor->SetPort(nullptr);
753 mActor = nullptr;
756 UpdateMustKeepAlive();
759 bool MessagePort::ConnectToPBackground() {
760 RefPtr<MessagePort> self = this;
761 auto raii = MakeScopeExit([self] {
762 self->mState = eStateDisentangled;
763 self->UpdateMustKeepAlive();
766 mozilla::ipc::PBackgroundChild* actorChild =
767 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
768 if (NS_WARN_IF(!actorChild)) {
769 return false;
772 PMessagePortChild* actor = actorChild->SendPMessagePortConstructor(
773 mIdentifier->uuid(), mIdentifier->destinationUuid(),
774 mIdentifier->sequenceId());
775 if (NS_WARN_IF(!actor)) {
776 return false;
779 mActor = static_cast<MessagePortChild*>(actor);
780 MOZ_ASSERT(mActor);
782 mActor->SetPort(this);
783 mState = eStateEntangling;
785 raii.release();
786 return true;
789 void MessagePort::UpdateMustKeepAlive() {
790 if (mState >= eStateDisentangled && mMessages.IsEmpty() && mIsKeptAlive) {
791 mIsKeptAlive = false;
793 // The DTOR of this WorkerRef will release the worker for us.
794 mWorkerRef = nullptr;
796 Release();
797 return;
800 if (mState < eStateDisentangled && !mIsKeptAlive) {
801 mIsKeptAlive = true;
802 AddRef();
806 void MessagePort::DisconnectFromOwner() {
807 CloseForced();
808 DOMEventTargetHelper::DisconnectFromOwner();
811 void MessagePort::RemoveDocFromBFCache() {
812 if (!NS_IsMainThread()) {
813 return;
816 if (nsPIDOMWindowInner* window = GetOwner()) {
817 window->RemoveFromBFCacheSync();
821 /* static */
822 void MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier) {
823 mozilla::ipc::PBackgroundChild* actorChild =
824 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
825 if (NS_WARN_IF(!actorChild)) {
826 MOZ_CRASH("Failed to create a PBackgroundChild actor!");
829 Unused << actorChild->SendMessagePortForceClose(aIdentifier.uuid(),
830 aIdentifier.destinationUuid(),
831 aIdentifier.sequenceId());
834 void MessagePort::DispatchError() {
835 nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
837 AutoJSAPI jsapi;
838 if (!globalObject || !jsapi.Init(globalObject)) {
839 NS_WARNING("Failed to initialize AutoJSAPI object.");
840 return;
843 RootedDictionary<MessageEventInit> init(jsapi.cx());
844 init.mBubbles = false;
845 init.mCancelable = false;
847 RefPtr<Event> event =
848 MessageEvent::Constructor(this, u"messageerror"_ns, init);
849 event->SetTrusted(true);
851 DispatchEvent(*event);
854 } // namespace mozilla::dom