tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MessagePort.cpp (24753B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "MessagePort.h"
      8 
      9 #include "MessageEvent.h"
     10 #include "MessagePortChild.h"
     11 #include "mozilla/ScopeExit.h"
     12 #include "mozilla/dom/BlobBinding.h"
     13 #include "mozilla/dom/Event.h"
     14 #include "mozilla/dom/File.h"
     15 #include "mozilla/dom/MessageChannel.h"
     16 #include "mozilla/dom/MessageEventBinding.h"
     17 #include "mozilla/dom/MessagePortBinding.h"
     18 #include "mozilla/dom/MessagePortChild.h"
     19 #include "mozilla/dom/PMessagePort.h"
     20 #include "mozilla/dom/RefMessageBodyService.h"
     21 #include "mozilla/dom/RootedDictionary.h"
     22 #include "mozilla/dom/ScriptSettings.h"
     23 #include "mozilla/dom/SharedMessageBody.h"
     24 #include "mozilla/dom/StructuredCloneTags.h"
     25 #include "mozilla/dom/WorkerCommon.h"
     26 #include "mozilla/dom/WorkerRef.h"
     27 #include "mozilla/dom/WorkerScope.h"
     28 #include "mozilla/ipc/BackgroundChild.h"
     29 #include "mozilla/ipc/PBackgroundChild.h"
     30 #include "nsContentUtils.h"
     31 #include "nsGlobalWindowInner.h"
     32 #include "nsPresContext.h"
     33 
     34 #ifdef XP_WIN
     35 #  undef PostMessage
     36 #endif
     37 
     38 namespace mozilla::dom {
     39 
     40 void UniqueMessagePortId::ForceClose() {
     41  if (!mIdentifier.neutered()) {
     42    MessagePort::ForceClose(mIdentifier);
     43    mIdentifier.neutered() = true;
     44  }
     45 }
     46 
     47 class PostMessageRunnable final : public CancelableRunnable {
     48  friend class MessagePort;
     49 
     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);
     57  }
     58 
     59  NS_IMETHOD
     60  Run() override {
     61    NS_ASSERT_OWNINGTHREAD(Runnable);
     62 
     63    // The port can be cycle collected while this runnable is pending in
     64    // the event queue.
     65    if (!mPort) {
     66      return NS_OK;
     67    }
     68 
     69    MOZ_ASSERT(mPort->mPostMessageRunnable == this);
     70 
     71    DispatchMessage();
     72 
     73    // We must check if we were waiting for this message in order to shutdown
     74    // the port.
     75    mPort->UpdateMustKeepAlive();
     76 
     77    mPort->mPostMessageRunnable = nullptr;
     78    mPort->Dispatch();
     79 
     80    return NS_OK;
     81  }
     82 
     83  nsresult Cancel() override {
     84    NS_ASSERT_OWNINGTHREAD(Runnable);
     85 
     86    mPort = nullptr;
     87    mData = nullptr;
     88    return NS_OK;
     89  }
     90 
     91 private:
     92  void DispatchMessage() const {
     93    NS_ASSERT_OWNINGTHREAD(Runnable);
     94 
     95    if (NS_FAILED(mPort->CheckCurrentGlobalCorrectness())) {
     96      return;
     97    }
     98 
     99    nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
    100 
    101    AutoJSAPI jsapi;
    102    if (!globalObject || !jsapi.Init(globalObject)) {
    103      NS_WARNING("Failed to initialize AutoJSAPI object.");
    104      return;
    105    }
    106 
    107    JSContext* cx = jsapi.cx();
    108 
    109    IgnoredErrorResult rv;
    110    JS::Rooted<JS::Value> value(cx);
    111 
    112    mData->Read(cx, &value, mPort->mRefMessageBodyService,
    113                SharedMessageBody::ReadMethod::StealRefMessageBody, rv);
    114 
    115    if (NS_WARN_IF(rv.Failed())) {
    116      JS_ClearPendingException(cx);
    117      mPort->DispatchError();
    118      return;
    119    }
    120 
    121    // Create the event
    122    RefPtr<MessageEvent> event = new MessageEvent(mPort, nullptr, nullptr);
    123 
    124    Sequence<OwningNonNull<MessagePort>> ports;
    125    if (!mData->TakeTransferredPortsAsSequence(ports)) {
    126      mPort->DispatchError();
    127      return;
    128    }
    129 
    130    event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
    131                            Cancelable::eNo, value, u""_ns, u""_ns, nullptr,
    132                            ports);
    133    event->SetTrusted(true);
    134 
    135    mPort->DispatchEvent(*event);
    136  }
    137 
    138 private:
    139  ~PostMessageRunnable() = default;
    140 
    141  RefPtr<MessagePort> mPort;
    142  RefPtr<SharedMessageBody> mData;
    143 };
    144 
    145 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
    146 
    147 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
    148                                                DOMEventTargetHelper)
    149  if (tmp->mPostMessageRunnable) {
    150    NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
    151  }
    152 
    153  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
    154 
    155  tmp->CloseForced();
    156 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    157 
    158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
    159                                                  DOMEventTargetHelper)
    160  if (tmp->mPostMessageRunnable) {
    161    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
    162  }
    163 
    164  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
    165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    166 
    167 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePort)
    168 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    169 
    170 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
    171 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
    172 
    173 MessagePort::MessagePort(nsIGlobalObject* aGlobal, State aState)
    174    : DOMEventTargetHelper(aGlobal),
    175      mRefMessageBodyService(RefMessageBodyService::GetOrCreate()),
    176      mState(aState),
    177      mMessageQueueEnabled(false),
    178      mIsKeptAlive(false),
    179      mHasBeenTransferredOrClosed(false) {
    180  MOZ_ASSERT(aGlobal);
    181 
    182  mIdentifier = MakeUnique<MessagePortIdentifier>();
    183  mIdentifier->neutered() = true;
    184  mIdentifier->sequenceId() = 0;
    185 }
    186 
    187 MessagePort::~MessagePort() {
    188  CloseForced();
    189  MOZ_ASSERT(!mActor);
    190  if (mActor) {
    191    mActor->SetPort(nullptr);
    192    mActor = nullptr;
    193  }
    194  MOZ_ASSERT(!mWorkerRef);
    195 }
    196 
    197 /* static */
    198 already_AddRefed<MessagePort> MessagePort::Create(nsIGlobalObject* aGlobal,
    199                                                  const nsID& aUUID,
    200                                                  const nsID& aDestinationUUID,
    201                                                  ErrorResult& aRv) {
    202  MOZ_ASSERT(aGlobal);
    203 
    204  RefPtr<MessagePort> mp =
    205      new MessagePort(aGlobal, eStateInitializingUnshippedEntangled);
    206  mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
    207                 false /* Neutered */, aRv);
    208  return mp.forget();
    209 }
    210 
    211 /* static */
    212 already_AddRefed<MessagePort> MessagePort::Create(
    213    nsIGlobalObject* aGlobal, UniqueMessagePortId& aIdentifier,
    214    ErrorResult& aRv) {
    215  MOZ_ASSERT(aGlobal);
    216 
    217  RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateEntangling);
    218  mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
    219                 aIdentifier.sequenceId(), aIdentifier.neutered(), aRv);
    220  aIdentifier.neutered() = true;
    221  return mp.forget();
    222 }
    223 
    224 void MessagePort::UnshippedEntangle(RefPtr<MessagePort>& aEntangledPort) {
    225  MOZ_DIAGNOSTIC_ASSERT(aEntangledPort);
    226  MOZ_DIAGNOSTIC_ASSERT(!mUnshippedEntangledPort);
    227 
    228  if (mState == eStateInitializingUnshippedEntangled) {
    229    mUnshippedEntangledPort = aEntangledPort;
    230    mState = eStateUnshippedEntangled;
    231  } else {
    232    MOZ_ASSERT_UNREACHABLE("Should not have been called.");
    233  }
    234 }
    235 
    236 void MessagePort::Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
    237                             uint32_t aSequenceID, bool aNeutered,
    238                             ErrorResult& aRv) {
    239  MOZ_ASSERT(mIdentifier);
    240  mIdentifier->uuid() = aUUID;
    241  mIdentifier->destinationUuid() = aDestinationUUID;
    242  mIdentifier->sequenceId() = aSequenceID;
    243 
    244  if (aNeutered) {
    245    // If this port is neutered we don't want to keep it alive artificially nor
    246    // we want to add listeners or WorkerRefs.
    247    mState = eStateDisentangled;
    248    return;
    249  }
    250 
    251  if (mState == eStateEntangling) {
    252    if (!ConnectToPBackground()) {
    253      aRv.Throw(NS_ERROR_FAILURE);
    254      return;
    255    }
    256  } else {
    257    MOZ_ASSERT(mState == eStateInitializingUnshippedEntangled);
    258  }
    259 
    260  // The port has to keep itself alive until it's entangled.
    261  UpdateMustKeepAlive();
    262 
    263  if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
    264    RefPtr<MessagePort> self = this;
    265 
    266    // When the callback is executed, we cannot process messages anymore because
    267    // we cannot dispatch new runnables. Let's force a Close().
    268    RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create(
    269        workerPrivate, "MessagePort", [self]() { self->CloseForced(); });
    270    if (NS_WARN_IF(!strongWorkerRef)) {
    271      // The worker is shutting down.
    272      CloseForced();
    273      aRv.Throw(NS_ERROR_FAILURE);
    274      return;
    275    }
    276 
    277    MOZ_ASSERT(!mWorkerRef);
    278    mWorkerRef = std::move(strongWorkerRef);
    279  }
    280 }
    281 
    282 JSObject* MessagePort::WrapObject(JSContext* aCx,
    283                                  JS::Handle<JSObject*> aGivenProto) {
    284  return MessagePort_Binding::Wrap(aCx, this, aGivenProto);
    285 }
    286 
    287 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
    288                              const Sequence<JSObject*>& aTransferable,
    289                              ErrorResult& aRv) {
    290  // We *must* clone the data here, or the JS::Value could be modified
    291  // by script
    292 
    293  // Here we want to check if the transerable object list contains
    294  // this port.
    295  for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
    296    JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
    297    if (!object) {
    298      continue;
    299    }
    300 
    301    MessagePort* port = nullptr;
    302    nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
    303    if (NS_SUCCEEDED(rv) && port == this) {
    304      aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
    305      return;
    306    }
    307  }
    308 
    309  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
    310 
    311  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
    312                                                          &transferable);
    313  if (NS_WARN_IF(aRv.Failed())) {
    314    return;
    315  }
    316 
    317  Maybe<nsID> agentClusterId;
    318  nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
    319  if (global) {
    320    agentClusterId = global->GetAgentClusterId();
    321  }
    322 
    323  RefPtr<SharedMessageBody> data = new SharedMessageBody(
    324      StructuredCloneHolder::TransferringSupported, agentClusterId);
    325 
    326  data->Write(aCx, aMessage, transferable, mIdentifier->uuid(),
    327              mRefMessageBodyService, aRv);
    328 
    329  if (NS_WARN_IF(aRv.Failed())) {
    330    return;
    331  }
    332 
    333  // This message has to be ignored.
    334  if (mState > eStateEntangled) {
    335    return;
    336  }
    337 
    338  if (mState == eStateInitializingUnshippedEntangled) {
    339    MOZ_ASSERT_UNREACHABLE(
    340        "Should be eStateUnshippedEntangled or eStateDisentangled by now.");
    341    return;
    342  }
    343 
    344  // If we are unshipped we are connected to the other port on the same thread.
    345  if (mState == eStateUnshippedEntangled) {
    346    MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
    347    mUnshippedEntangledPort->mMessages.AppendElement(data);
    348    mUnshippedEntangledPort->Dispatch();
    349    return;
    350  }
    351 
    352  // Not entangled yet, but already closed/disentangled.
    353  if (mState == eStateEntanglingForDisentangle ||
    354      mState == eStateEntanglingForClose) {
    355    return;
    356  }
    357 
    358  RemoveDocFromBFCache();
    359 
    360  // Not entangled yet.
    361  if (mState == eStateEntangling) {
    362    mMessagesForTheOtherPort.AppendElement(data);
    363    return;
    364  }
    365 
    366  MOZ_ASSERT(mActor);
    367  MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
    368 
    369  AutoTArray<RefPtr<SharedMessageBody>, 1> array;
    370  array.AppendElement(data);
    371 
    372  AutoTArray<MessageData, 1> messages;
    373  // note: `messages` will borrow the underlying buffer, but this is okay
    374  // because reverse destruction order means `messages` will be destroyed prior
    375  // to `array`/`data`.
    376  SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), array,
    377                                               messages);
    378  mActor->SendPostMessages(messages);
    379 }
    380 
    381 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
    382                              const StructuredSerializeOptions& aOptions,
    383                              ErrorResult& aRv) {
    384  PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
    385 }
    386 
    387 void MessagePort::Start() {
    388  if (mMessageQueueEnabled) {
    389    return;
    390  }
    391 
    392  mMessageQueueEnabled = true;
    393  Dispatch();
    394 }
    395 
    396 void MessagePort::Dispatch() {
    397  if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
    398    return;
    399  }
    400 
    401  switch (mState) {
    402    case eStateInitializingUnshippedEntangled:
    403      MOZ_ASSERT_UNREACHABLE(
    404          "Should be eStateUnshippedEntangled or eStateDisentangled by now.");
    405      break;
    406 
    407    case eStateUnshippedEntangled:
    408      // Everything is fine here. We have messages because the other
    409      // port populates our queue directly.
    410      break;
    411 
    412    case eStateEntangling:
    413      // Everything is fine here as well. We have messages because the other
    414      // port populated our queue directly when we were in the
    415      // eStateUnshippedEntangled state.
    416      break;
    417 
    418    case eStateEntanglingForDisentangle:
    419      // Here we don't want to ship messages because these messages must be
    420      // delivered by the cloned version of this one. They will be sent in the
    421      // SendDisentangle().
    422      return;
    423 
    424    case eStateEntanglingForClose:
    425      // We still want to deliver messages if we are closing. These messages
    426      // are here from the previous eStateUnshippedEntangled state.
    427      break;
    428 
    429    case eStateEntangled:
    430      // This port is up and running.
    431      break;
    432 
    433    case eStateDisentangling:
    434      // If we are in the process to disentangle the port, we cannot dispatch
    435      // messages. They will be sent to the cloned version of this port via
    436      // SendDisentangle();
    437      return;
    438 
    439    case eStateDisentangled:
    440      MOZ_CRASH("This cannot happen.");
    441      // It cannot happen because Disentangle should take off all the pending
    442      // messages.
    443      break;
    444 
    445    case eStateDisentangledForClose:
    446      // If we are here is because the port has been closed. We can still
    447      // process the pending messages.
    448      break;
    449  }
    450 
    451  RefPtr<SharedMessageBody> data = mMessages.ElementAt(0);
    452  mMessages.RemoveElementAt(0);
    453 
    454  mPostMessageRunnable = new PostMessageRunnable(this, data);
    455  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
    456 }
    457 
    458 void MessagePort::Close() {
    459  mHasBeenTransferredOrClosed = true;
    460  CloseInternal(true /* aSoftly */);
    461 }
    462 
    463 void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); }
    464 
    465 void MessagePort::CloseInternal(bool aSoftly) {
    466  // If we have some messages to send but we don't want a 'soft' close, we have
    467  // to flush them now.
    468  if (!aSoftly) {
    469    mMessages.Clear();
    470  }
    471 
    472  // Let's inform the RefMessageBodyService that any our shared messages are
    473  // now invalid.
    474  mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
    475 
    476  if (mState == eStateInitializingUnshippedEntangled) {
    477    // We can end up here if we failed to create our WorkerRef.  Our Create
    478    // method will end up returning an error and MessageChannel will not bother
    479    // creating our counterpart port or calling UnshippedEntangle (and we
    480    // assert on this).
    481    mState = eStateDisentangledForClose;
    482 
    483    UpdateMustKeepAlive();
    484    return;
    485  }
    486 
    487  if (mState == eStateUnshippedEntangled) {
    488    MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort);
    489 
    490    // This avoids loops.
    491    RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
    492 
    493    mState = eStateDisentangledForClose;
    494    port->CloseInternal(aSoftly);
    495 
    496    UpdateMustKeepAlive();
    497    return;
    498  }
    499 
    500  // Not entangled yet, we have to wait.
    501  if (mState == eStateEntangling) {
    502    mState = eStateEntanglingForClose;
    503    return;
    504  }
    505 
    506  // Not entangled but already cloned or closed
    507  if (mState == eStateEntanglingForDisentangle ||
    508      mState == eStateEntanglingForClose) {
    509    return;
    510  }
    511 
    512  // Maybe we were already closing the port but softly. In this case we call
    513  // UpdateMustKeepAlive() to consider the empty pending message queue.
    514  if (mState == eStateDisentangledForClose && !aSoftly) {
    515    UpdateMustKeepAlive();
    516    return;
    517  }
    518 
    519  if (mState > eStateEntangled) {
    520    return;
    521  }
    522 
    523  // We don't care about stopping the sending of messages because from now all
    524  // the incoming messages will be ignored.
    525  mState = eStateDisentangledForClose;
    526 
    527  MOZ_ASSERT(mActor);
    528 
    529  mActor->SendClose();
    530  mActor->SetPort(nullptr);
    531  mActor = nullptr;
    532 
    533  UpdateMustKeepAlive();
    534 }
    535 
    536 EventHandlerNonNull* MessagePort::GetOnmessage() {
    537  return GetEventHandler(nsGkAtoms::onmessage);
    538 }
    539 
    540 void MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) {
    541  SetEventHandler(nsGkAtoms::onmessage, aCallback);
    542 
    543  // When using onmessage, the call to start() is implied.
    544  Start();
    545 }
    546 
    547 // This method is called when the PMessagePortChild actor is entangled to
    548 // another actor. It receives a list of messages to be dispatch. It can be that
    549 // we were waiting for this entangling step in order to disentangle the port or
    550 // to close it.
    551 void MessagePort::Entangled(nsTArray<MessageData>& aMessages) {
    552  MOZ_ASSERT(mState == eStateEntangling ||
    553             mState == eStateEntanglingForDisentangle ||
    554             mState == eStateEntanglingForClose);
    555 
    556  State oldState = mState;
    557  mState = eStateEntangled;
    558 
    559  // If we have pending messages, these have to be sent.
    560  if (!mMessagesForTheOtherPort.IsEmpty()) {
    561    {
    562      nsTArray<MessageData> messages;
    563      SharedMessageBody::FromSharedToMessagesChild(
    564          mActor->Manager(), mMessagesForTheOtherPort, messages);
    565      mActor->SendPostMessages(messages);
    566    }
    567    // Because `messages` borrow the underlying JSStructuredCloneData buffers,
    568    // only clear after `messages` have gone out of scope.
    569    mMessagesForTheOtherPort.Clear();
    570  }
    571 
    572  // We must convert the messages into SharedMessageBodys to avoid leaks.
    573  FallibleTArray<RefPtr<SharedMessageBody>> data;
    574  if (NS_WARN_IF(
    575          !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
    576    DispatchError();
    577    return;
    578  }
    579 
    580  // If the next step is to close the port, we do it ignoring the received
    581  // messages.
    582  if (oldState == eStateEntanglingForClose) {
    583    CloseForced();
    584    return;
    585  }
    586 
    587  mMessages.AppendElements(data);
    588 
    589  // We were waiting for the entangling callback in order to disentangle this
    590  // port immediately after.
    591  if (oldState == eStateEntanglingForDisentangle) {
    592    StartDisentangling();
    593    return;
    594  }
    595 
    596  Dispatch();
    597 }
    598 
    599 void MessagePort::StartDisentangling() {
    600  MOZ_ASSERT(mActor);
    601  MOZ_ASSERT(mState == eStateEntangled);
    602 
    603  mState = eStateDisentangling;
    604 
    605  // Sending this message we communicate to the parent actor that we don't want
    606  // to receive any new messages. It is possible that a message has been
    607  // already sent but not received yet. So we have to collect all of them and
    608  // we send them in the SendDispatch() request.
    609  mActor->SendStopSendingData();
    610 }
    611 
    612 void MessagePort::MessagesReceived(nsTArray<MessageData>& aMessages) {
    613  MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling ||
    614             // This last step can happen only if Close() has been called
    615             // manually. At this point SendClose() is sent but we can still
    616             // receive something until the Closing request is processed.
    617             mState == eStateDisentangledForClose);
    618  MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
    619 
    620  RemoveDocFromBFCache();
    621 
    622  FallibleTArray<RefPtr<SharedMessageBody>> data;
    623  if (NS_WARN_IF(
    624          !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) {
    625    DispatchError();
    626    return;
    627  }
    628 
    629  mMessages.AppendElements(data);
    630 
    631  if (mState == eStateEntangled) {
    632    Dispatch();
    633  }
    634 }
    635 
    636 void MessagePort::StopSendingDataConfirmed() {
    637  MOZ_ASSERT(mState == eStateDisentangling);
    638  MOZ_ASSERT(mActor);
    639 
    640  Disentangle();
    641 }
    642 
    643 void MessagePort::Disentangle() {
    644  MOZ_ASSERT(mState == eStateDisentangling);
    645  MOZ_ASSERT(mActor);
    646 
    647  mState = eStateDisentangled;
    648 
    649  {
    650    nsTArray<MessageData> messages;
    651    SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), mMessages,
    652                                                 messages);
    653    mActor->SendDisentangle(messages);
    654  }
    655 
    656  // Let's inform the RefMessageBodyService that any our shared messages are
    657  // now invalid.
    658  mRefMessageBodyService->ForgetPort(mIdentifier->uuid());
    659 
    660  // Only clear mMessages after the MessageData instances have gone out of scope
    661  // because they borrow mMessages' underlying JSStructuredCloneDatas.
    662  mMessages.Clear();
    663 
    664  mActor->SetPort(nullptr);
    665  mActor = nullptr;
    666 
    667  UpdateMustKeepAlive();
    668 }
    669 
    670 void MessagePort::CloneAndDisentangle(UniqueMessagePortId& aIdentifier) {
    671  MOZ_ASSERT(mIdentifier);
    672  MOZ_ASSERT(!mHasBeenTransferredOrClosed);
    673 
    674  mHasBeenTransferredOrClosed = true;
    675 
    676  // We can clone a port that has already been transfered. In this case, on the
    677  // otherside will have a neutered port. Here we set neutered to true so that
    678  // we are safe in case a early return.
    679  aIdentifier.neutered() = true;
    680 
    681  if (mState > eStateEntangled) {
    682    return;
    683  }
    684 
    685  // We already have a 'next step'. We have to consider this port as already
    686  // cloned/closed/disentangled.
    687  if (mState == eStateEntanglingForDisentangle ||
    688      mState == eStateEntanglingForClose) {
    689    return;
    690  }
    691 
    692  aIdentifier.uuid() = mIdentifier->uuid();
    693  aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
    694  aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
    695  aIdentifier.neutered() = false;
    696 
    697  // We have to entangle first.
    698  if (mState == eStateUnshippedEntangled) {
    699    MOZ_ASSERT(mUnshippedEntangledPort);
    700    MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
    701 
    702    RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
    703 
    704    // Disconnect the entangled port and connect it to PBackground.
    705    if (!port->ConnectToPBackground()) {
    706      // We are probably shutting down. We cannot proceed.
    707      mState = eStateDisentangled;
    708      UpdateMustKeepAlive();
    709      return;
    710    }
    711 
    712    // In this case, we don't need to be connected to the PBackground service.
    713    if (mMessages.IsEmpty()) {
    714      aIdentifier.sequenceId() = mIdentifier->sequenceId();
    715 
    716      mState = eStateDisentangled;
    717      UpdateMustKeepAlive();
    718      return;
    719    }
    720 
    721    // Register this component to PBackground.
    722    if (!ConnectToPBackground()) {
    723      // We are probably shutting down. We cannot proceed.
    724      return;
    725    }
    726 
    727    mState = eStateEntanglingForDisentangle;
    728    return;
    729  }
    730 
    731  // Not entangled yet, we have to wait.
    732  if (mState == eStateEntangling) {
    733    mState = eStateEntanglingForDisentangle;
    734    return;
    735  }
    736 
    737  MOZ_ASSERT(mState == eStateEntangled);
    738  StartDisentangling();
    739 }
    740 
    741 void MessagePort::Closed() {
    742  if (mState >= eStateDisentangled) {
    743    return;
    744  }
    745 
    746  mState = eStateDisentangledForClose;
    747 
    748  if (mActor) {
    749    mActor->SetPort(nullptr);
    750    mActor = nullptr;
    751  }
    752 
    753  UpdateMustKeepAlive();
    754 }
    755 
    756 bool MessagePort::ConnectToPBackground() {
    757  RefPtr<MessagePort> self = this;
    758  auto raii = MakeScopeExit([self] {
    759    self->mState = eStateDisentangled;
    760    self->UpdateMustKeepAlive();
    761  });
    762 
    763  mozilla::ipc::PBackgroundChild* actorChild =
    764      mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    765  if (NS_WARN_IF(!actorChild)) {
    766    return false;
    767  }
    768 
    769  PMessagePortChild* actor = actorChild->SendPMessagePortConstructor(
    770      mIdentifier->uuid(), mIdentifier->destinationUuid(),
    771      mIdentifier->sequenceId());
    772  if (NS_WARN_IF(!actor)) {
    773    return false;
    774  }
    775 
    776  mActor = static_cast<MessagePortChild*>(actor);
    777  MOZ_ASSERT(mActor);
    778 
    779  mActor->SetPort(this);
    780  mState = eStateEntangling;
    781 
    782  raii.release();
    783  return true;
    784 }
    785 
    786 void MessagePort::UpdateMustKeepAlive() {
    787  if (mState >= eStateDisentangled && mMessages.IsEmpty() && mIsKeptAlive) {
    788    mIsKeptAlive = false;
    789 
    790    // The DTOR of this WorkerRef will release the worker for us.
    791    mWorkerRef = nullptr;
    792 
    793    Release();
    794    return;
    795  }
    796 
    797  if (mState < eStateDisentangled && !mIsKeptAlive) {
    798    mIsKeptAlive = true;
    799    AddRef();
    800  }
    801 }
    802 
    803 void MessagePort::DisconnectFromOwner() {
    804  CloseForced();
    805  DOMEventTargetHelper::DisconnectFromOwner();
    806 }
    807 
    808 void MessagePort::RemoveDocFromBFCache() {
    809  if (!NS_IsMainThread()) {
    810    return;
    811  }
    812 
    813  if (nsPIDOMWindowInner* window = GetOwnerWindow()) {
    814    window->RemoveFromBFCacheSync();
    815  }
    816 }
    817 
    818 /* static */
    819 void MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier) {
    820  mozilla::ipc::PBackgroundChild* actorChild =
    821      mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    822  if (NS_WARN_IF(!actorChild)) {
    823    // We are shutting down this process. This port will be leaked.
    824    return;
    825  }
    826 
    827  (void)actorChild->SendMessagePortForceClose(aIdentifier.uuid(),
    828                                              aIdentifier.destinationUuid(),
    829                                              aIdentifier.sequenceId());
    830 }
    831 
    832 void MessagePort::DispatchError() {
    833  nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
    834 
    835  AutoJSAPI jsapi;
    836  if (!globalObject || !jsapi.Init(globalObject)) {
    837    NS_WARNING("Failed to initialize AutoJSAPI object.");
    838    return;
    839  }
    840 
    841  RootedDictionary<MessageEventInit> init(jsapi.cx());
    842  init.mBubbles = false;
    843  init.mCancelable = false;
    844 
    845  RefPtr<Event> event =
    846      MessageEvent::Constructor(this, u"messageerror"_ns, init);
    847  event->SetTrusted(true);
    848 
    849  DispatchEvent(*event);
    850 }
    851 
    852 }  // namespace mozilla::dom