LCOV - code coverage report
Current view: top level - dom/messagechannel - MessagePort.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 448 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 53 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/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/ScriptSettings.h"
      20             : #include "mozilla/dom/StructuredCloneTags.h"
      21             : #include "mozilla/dom/WorkerPrivate.h"
      22             : #include "mozilla/dom/WorkerScope.h"
      23             : #include "mozilla/ipc/BackgroundChild.h"
      24             : #include "mozilla/ipc/PBackgroundChild.h"
      25             : #include "mozilla/MessagePortTimelineMarker.h"
      26             : #include "mozilla/TimelineConsumers.h"
      27             : #include "mozilla/TimelineMarker.h"
      28             : #include "mozilla/Unused.h"
      29             : #include "nsContentUtils.h"
      30             : #include "nsGlobalWindow.h"
      31             : #include "nsPresContext.h"
      32             : #include "SharedMessagePortMessage.h"
      33             : 
      34             : #include "nsIBFCacheEntry.h"
      35             : #include "nsIDocument.h"
      36             : #include "nsIDOMFileList.h"
      37             : #include "nsIPresShell.h"
      38             : #include "nsISupportsPrimitives.h"
      39             : #include "nsServiceManagerUtils.h"
      40             : 
      41             : #ifdef XP_WIN
      42             : #undef PostMessage
      43             : #endif
      44             : 
      45             : using namespace mozilla::dom::workers;
      46             : 
      47             : namespace mozilla {
      48             : namespace dom {
      49             : 
      50             : class PostMessageRunnable final : public CancelableRunnable
      51             : {
      52             :   friend class MessagePort;
      53             : 
      54             : public:
      55           0 :   PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
      56           0 :     : CancelableRunnable("dom::PostMessageRunnable")
      57             :     , mPort(aPort)
      58           0 :     , mData(aData)
      59             :   {
      60           0 :     MOZ_ASSERT(aPort);
      61           0 :     MOZ_ASSERT(aData);
      62           0 :   }
      63             : 
      64             :   NS_IMETHOD
      65           0 :   Run() override
      66             :   {
      67           0 :     NS_ASSERT_OWNINGTHREAD(Runnable);
      68             : 
      69             :     // The port can be cycle collected while this runnable is pending in
      70             :     // the event queue.
      71           0 :     if (!mPort) {
      72           0 :       return NS_OK;
      73             :     }
      74             : 
      75           0 :     MOZ_ASSERT(mPort->mPostMessageRunnable == this);
      76             : 
      77           0 :     nsresult rv = DispatchMessage();
      78             : 
      79             :     // We must check if we were waiting for this message in order to shutdown
      80             :     // the port.
      81           0 :     mPort->UpdateMustKeepAlive();
      82             : 
      83           0 :     mPort->mPostMessageRunnable = nullptr;
      84           0 :     mPort->Dispatch();
      85             : 
      86           0 :     return rv;
      87             :   }
      88             : 
      89             :   nsresult
      90           0 :   Cancel() override
      91             :   {
      92           0 :     NS_ASSERT_OWNINGTHREAD(Runnable);
      93             : 
      94           0 :     mPort = nullptr;
      95           0 :     mData = nullptr;
      96           0 :     return NS_OK;
      97             :   }
      98             : 
      99             : private:
     100             :   nsresult
     101           0 :   DispatchMessage() const
     102             :   {
     103           0 :     NS_ASSERT_OWNINGTHREAD(Runnable);
     104             : 
     105           0 :     nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
     106             : 
     107           0 :     AutoJSAPI jsapi;
     108           0 :     if (!globalObject || !jsapi.Init(globalObject)) {
     109           0 :       NS_WARNING("Failed to initialize AutoJSAPI object.");
     110           0 :       return NS_ERROR_FAILURE;
     111             :     }
     112             : 
     113           0 :     JSContext* cx = jsapi.cx();
     114             : 
     115           0 :     ErrorResult rv;
     116           0 :     JS::Rooted<JS::Value> value(cx);
     117             : 
     118           0 :     UniquePtr<AbstractTimelineMarker> start;
     119           0 :     UniquePtr<AbstractTimelineMarker> end;
     120           0 :     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
     121           0 :     bool isTimelineRecording = timelines && !timelines->IsEmpty();
     122             : 
     123           0 :     if (isTimelineRecording) {
     124           0 :       start = MakeUnique<MessagePortTimelineMarker>(
     125             :         ProfileTimelineMessagePortOperationType::DeserializeData,
     126           0 :         MarkerTracingType::START);
     127             :     }
     128             : 
     129           0 :     mData->Read(cx, &value, rv);
     130             : 
     131           0 :     if (isTimelineRecording) {
     132           0 :       end = MakeUnique<MessagePortTimelineMarker>(
     133             :         ProfileTimelineMessagePortOperationType::DeserializeData,
     134           0 :         MarkerTracingType::END);
     135           0 :       timelines->AddMarkerForAllObservedDocShells(start);
     136           0 :       timelines->AddMarkerForAllObservedDocShells(end);
     137             :     }
     138             : 
     139           0 :     if (NS_WARN_IF(rv.Failed())) {
     140           0 :       return rv.StealNSResult();
     141             :     }
     142             : 
     143             :     // Create the event
     144             :     nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
     145           0 :       do_QueryInterface(mPort->GetOwner());
     146             :     RefPtr<MessageEvent> event =
     147           0 :       new MessageEvent(eventTarget, nullptr, nullptr);
     148             : 
     149           0 :     Sequence<OwningNonNull<MessagePort>> ports;
     150           0 :     if (!mData->TakeTransferredPortsAsSequence(ports)) {
     151           0 :       return NS_ERROR_OUT_OF_MEMORY;
     152             :     }
     153             : 
     154           0 :     event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"),
     155             :                             false /* non-bubbling */,
     156           0 :                             false /* cancelable */, value, EmptyString(),
     157           0 :                             EmptyString(), nullptr, ports);
     158           0 :     event->SetTrusted(true);
     159             : 
     160             :     bool dummy;
     161           0 :     mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
     162             : 
     163           0 :     return NS_OK;
     164             :   }
     165             : 
     166             : private:
     167           0 :   ~PostMessageRunnable()
     168           0 :   {}
     169             : 
     170             :   RefPtr<MessagePort> mPort;
     171             :   RefPtr<SharedMessagePortMessage> mData;
     172             : };
     173             : 
     174             : NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
     175             : 
     176           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
     177             :                                                 DOMEventTargetHelper)
     178           0 :   if (tmp->mPostMessageRunnable) {
     179           0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
     180             :   }
     181             : 
     182           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
     183           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
     184           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
     185           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     186             : 
     187           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
     188             :                                                   DOMEventTargetHelper)
     189           0 :   if (tmp->mPostMessageRunnable) {
     190           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
     191             :   }
     192             : 
     193           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
     194           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     195             : 
     196           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
     197           0 :   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
     198           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     199           0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     200             : 
     201           0 : NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
     202           0 : NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
     203             : 
     204             : namespace {
     205             : 
     206             : class MessagePortWorkerHolder final : public workers::WorkerHolder
     207             : {
     208             :   MessagePort* mPort;
     209             : 
     210             : public:
     211           0 :   explicit MessagePortWorkerHolder(MessagePort* aPort)
     212           0 :     : mPort(aPort)
     213             :   {
     214           0 :     MOZ_ASSERT(aPort);
     215           0 :     MOZ_COUNT_CTOR(MessagePortWorkerHolder);
     216           0 :   }
     217             : 
     218           0 :   virtual bool Notify(workers::Status aStatus) override
     219             :   {
     220           0 :     if (aStatus > Running) {
     221             :       // We cannot process messages anymore because we cannot dispatch new
     222             :       // runnables. Let's force a Close().
     223           0 :       mPort->CloseForced();
     224             :     }
     225             : 
     226           0 :     return true;
     227             :   }
     228             : 
     229             : private:
     230           0 :   ~MessagePortWorkerHolder()
     231           0 :   {
     232           0 :     MOZ_COUNT_DTOR(MessagePortWorkerHolder);
     233           0 :   }
     234             : };
     235             : 
     236             : class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback
     237             : {
     238             : public:
     239             :   NS_DECL_ISUPPORTS
     240             : 
     241           0 :   static void ForceClose(const MessagePortIdentifier& aIdentifier)
     242             :   {
     243             :     PBackgroundChild* actor =
     244           0 :       mozilla::ipc::BackgroundChild::GetForCurrentThread();
     245           0 :     if (actor) {
     246           0 :       Unused << actor->SendMessagePortForceClose(aIdentifier.uuid(),
     247             :                                                  aIdentifier.destinationUuid(),
     248             :                                                  aIdentifier.sequenceId());
     249           0 :       return;
     250             :     }
     251             : 
     252           0 :     RefPtr<ForceCloseHelper> helper = new ForceCloseHelper(aIdentifier);
     253           0 :     if (NS_WARN_IF(!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(helper))) {
     254           0 :       MOZ_CRASH();
     255             :     }
     256             :   }
     257             : 
     258             : private:
     259           0 :   explicit ForceCloseHelper(const MessagePortIdentifier& aIdentifier)
     260           0 :     : mIdentifier(aIdentifier)
     261           0 :   {}
     262             : 
     263           0 :   ~ForceCloseHelper() {}
     264             : 
     265           0 :   void ActorFailed() override
     266             :   {
     267           0 :     MOZ_CRASH("Failed to create a PBackgroundChild actor!");
     268             :   }
     269             : 
     270           0 :   void ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override
     271             :   {
     272           0 :     ForceClose(mIdentifier);
     273           0 :   }
     274             : 
     275             :   const MessagePortIdentifier mIdentifier;
     276             : };
     277             : 
     278           0 : NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
     279             : 
     280             : } // namespace
     281             : 
     282           0 : MessagePort::MessagePort(nsIGlobalObject* aGlobal)
     283             :   : DOMEventTargetHelper(aGlobal)
     284             :   , mInnerID(0)
     285             :   , mMessageQueueEnabled(false)
     286           0 :   , mIsKeptAlive(false)
     287             : {
     288           0 :   MOZ_ASSERT(aGlobal);
     289             : 
     290           0 :   mIdentifier = new MessagePortIdentifier();
     291           0 :   mIdentifier->neutered() = true;
     292           0 :   mIdentifier->sequenceId() = 0;
     293           0 : }
     294             : 
     295           0 : MessagePort::~MessagePort()
     296             : {
     297           0 :   CloseForced();
     298           0 :   MOZ_ASSERT(!mWorkerHolder);
     299           0 : }
     300             : 
     301             : /* static */ already_AddRefed<MessagePort>
     302           0 : MessagePort::Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
     303             :                     const nsID& aDestinationUUID, ErrorResult& aRv)
     304             : {
     305           0 :   MOZ_ASSERT(aGlobal);
     306             : 
     307           0 :   RefPtr<MessagePort> mp = new MessagePort(aGlobal);
     308           0 :   mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
     309           0 :                  false /* Neutered */, eStateUnshippedEntangled, aRv);
     310           0 :   return mp.forget();
     311             : }
     312             : 
     313             : /* static */ already_AddRefed<MessagePort>
     314           0 : MessagePort::Create(nsIGlobalObject* aGlobal,
     315             :                     const MessagePortIdentifier& aIdentifier,
     316             :                     ErrorResult& aRv)
     317             : {
     318           0 :   MOZ_ASSERT(aGlobal);
     319             : 
     320           0 :   RefPtr<MessagePort> mp = new MessagePort(aGlobal);
     321           0 :   mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
     322           0 :                  aIdentifier.sequenceId(), aIdentifier.neutered(),
     323           0 :                  eStateEntangling, aRv);
     324           0 :   return mp.forget();
     325             : }
     326             : 
     327             : void
     328           0 : MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
     329             : {
     330           0 :   MOZ_ASSERT(aEntangledPort);
     331           0 :   MOZ_ASSERT(!mUnshippedEntangledPort);
     332             : 
     333           0 :   mUnshippedEntangledPort = aEntangledPort;
     334           0 : }
     335             : 
     336             : void
     337           0 : MessagePort::Initialize(const nsID& aUUID,
     338             :                         const nsID& aDestinationUUID,
     339             :                         uint32_t aSequenceID, bool mNeutered,
     340             :                         State aState, ErrorResult& aRv)
     341             : {
     342           0 :   MOZ_ASSERT(mIdentifier);
     343           0 :   mIdentifier->uuid() = aUUID;
     344           0 :   mIdentifier->destinationUuid() = aDestinationUUID;
     345           0 :   mIdentifier->sequenceId() = aSequenceID;
     346             : 
     347           0 :   mState = aState;
     348             : 
     349           0 :   if (mNeutered) {
     350             :     // If this port is neutered we don't want to keep it alive artificially nor
     351             :     // we want to add listeners or workerWorkerHolders.
     352           0 :     mState = eStateDisentangled;
     353           0 :     return;
     354             :   }
     355             : 
     356           0 :   if (mState == eStateEntangling) {
     357           0 :     ConnectToPBackground();
     358             :   } else {
     359           0 :     MOZ_ASSERT(mState == eStateUnshippedEntangled);
     360             :   }
     361             : 
     362             :   // The port has to keep itself alive until it's entangled.
     363           0 :   UpdateMustKeepAlive();
     364             : 
     365           0 :   if (!NS_IsMainThread()) {
     366           0 :     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     367           0 :     MOZ_ASSERT(workerPrivate);
     368           0 :     MOZ_ASSERT(!mWorkerHolder);
     369             : 
     370           0 :     nsAutoPtr<WorkerHolder> workerHolder(new MessagePortWorkerHolder(this));
     371           0 :     if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate, Closing))) {
     372           0 :       aRv.Throw(NS_ERROR_FAILURE);
     373           0 :       return;
     374             :     }
     375             : 
     376           0 :     mWorkerHolder = Move(workerHolder);
     377           0 :   } else if (GetOwner()) {
     378           0 :     MOZ_ASSERT(NS_IsMainThread());
     379           0 :     MOZ_ASSERT(GetOwner()->IsInnerWindow());
     380           0 :     mInnerID = GetOwner()->WindowID();
     381             : 
     382           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     383           0 :     if (obs) {
     384           0 :       obs->AddObserver(this, "inner-window-destroyed", false);
     385             :     }
     386             :   }
     387             : }
     388             : 
     389             : JSObject*
     390           0 : MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     391             : {
     392           0 :   return MessagePortBinding::Wrap(aCx, this, aGivenProto);
     393             : }
     394             : 
     395             : void
     396           0 : MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
     397             :                          const Sequence<JSObject*>& aTransferable,
     398             :                          ErrorResult& aRv)
     399             : {
     400             :   // We *must* clone the data here, or the JS::Value could be modified
     401             :   // by script
     402             : 
     403             :   // Here we want to check if the transerable object list contains
     404             :   // this port.
     405           0 :   for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
     406           0 :     JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
     407           0 :     if (!object) {
     408           0 :       continue;
     409             :     }
     410             : 
     411           0 :     MessagePort* port = nullptr;
     412           0 :     nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
     413           0 :     if (NS_SUCCEEDED(rv) && port == this) {
     414           0 :       aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     415           0 :       return;
     416             :     }
     417             :   }
     418             : 
     419           0 :   JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
     420             : 
     421           0 :   aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
     422           0 :                                                           &transferable);
     423           0 :   if (NS_WARN_IF(aRv.Failed())) {
     424           0 :     return;
     425             :   }
     426             : 
     427           0 :   RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
     428             : 
     429           0 :   UniquePtr<AbstractTimelineMarker> start;
     430           0 :   UniquePtr<AbstractTimelineMarker> end;
     431           0 :   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
     432           0 :   bool isTimelineRecording = timelines && !timelines->IsEmpty();
     433             : 
     434           0 :   if (isTimelineRecording) {
     435           0 :     start = MakeUnique<MessagePortTimelineMarker>(
     436             :       ProfileTimelineMessagePortOperationType::SerializeData,
     437           0 :       MarkerTracingType::START);
     438             :   }
     439             : 
     440           0 :   data->Write(aCx, aMessage, transferable, aRv);
     441             : 
     442           0 :   if (isTimelineRecording) {
     443           0 :     end = MakeUnique<MessagePortTimelineMarker>(
     444             :       ProfileTimelineMessagePortOperationType::SerializeData,
     445           0 :       MarkerTracingType::END);
     446           0 :     timelines->AddMarkerForAllObservedDocShells(start);
     447           0 :     timelines->AddMarkerForAllObservedDocShells(end);
     448             :   }
     449             : 
     450           0 :   if (NS_WARN_IF(aRv.Failed())) {
     451           0 :     return;
     452             :   }
     453             : 
     454             :   // This message has to be ignored.
     455           0 :   if (mState > eStateEntangled) {
     456           0 :     return;
     457             :   }
     458             : 
     459             :   // If we are unshipped we are connected to the other port on the same thread.
     460           0 :   if (mState == eStateUnshippedEntangled) {
     461           0 :     MOZ_ASSERT(mUnshippedEntangledPort);
     462           0 :     mUnshippedEntangledPort->mMessages.AppendElement(data);
     463           0 :     mUnshippedEntangledPort->Dispatch();
     464           0 :     return;
     465             :   }
     466             : 
     467             :   // Not entangled yet, but already closed/disentangled.
     468           0 :   if (mState == eStateEntanglingForDisentangle ||
     469           0 :       mState == eStateEntanglingForClose) {
     470           0 :     return;
     471             :   }
     472             : 
     473           0 :   RemoveDocFromBFCache();
     474             : 
     475             :   // Not entangled yet.
     476           0 :   if (mState == eStateEntangling) {
     477           0 :     mMessagesForTheOtherPort.AppendElement(data);
     478           0 :     return;
     479             :   }
     480             : 
     481           0 :   MOZ_ASSERT(mActor);
     482           0 :   MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
     483             : 
     484           0 :   AutoTArray<RefPtr<SharedMessagePortMessage>, 1> array;
     485           0 :   array.AppendElement(data);
     486             : 
     487           0 :   AutoTArray<ClonedMessageData, 1> messages;
     488             :   // note: `messages` will borrow the underlying buffer, but this is okay
     489             :   // because reverse destruction order means `messages` will be destroyed prior
     490             :   // to `array`/`data`.
     491           0 :   SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
     492           0 :   mActor->SendPostMessages(messages);
     493             : }
     494             : 
     495             : void
     496           0 : MessagePort::Start()
     497             : {
     498           0 :   if (mMessageQueueEnabled) {
     499           0 :     return;
     500             :   }
     501             : 
     502           0 :   mMessageQueueEnabled = true;
     503           0 :   Dispatch();
     504             : }
     505             : 
     506             : void
     507           0 : MessagePort::Dispatch()
     508             : {
     509           0 :   if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
     510           0 :     return;
     511             :   }
     512             : 
     513           0 :   switch (mState) {
     514             :     case eStateUnshippedEntangled:
     515             :       // Everything is fine here. We have messages because the other
     516             :       // port populates our queue directly.
     517           0 :       break;
     518             : 
     519             :     case eStateEntangling:
     520             :       // Everything is fine here as well. We have messages because the other
     521             :       // port populated our queue directly when we were in the
     522             :       // eStateUnshippedEntangled state.
     523           0 :       break;
     524             : 
     525             :     case eStateEntanglingForDisentangle:
     526             :       // Here we don't want to ship messages because these messages must be
     527             :       // delivered by the cloned version of this one. They will be sent in the
     528             :       // SendDisentangle().
     529           0 :       return;
     530             : 
     531             :     case eStateEntanglingForClose:
     532             :       // We still want to deliver messages if we are closing. These messages
     533             :       // are here from the previous eStateUnshippedEntangled state.
     534           0 :       break;
     535             : 
     536             :     case eStateEntangled:
     537             :       // This port is up and running.
     538           0 :       break;
     539             : 
     540             :     case eStateDisentangling:
     541             :       // If we are in the process to disentangle the port, we cannot dispatch
     542             :       // messages. They will be sent to the cloned version of this port via
     543             :       // SendDisentangle();
     544           0 :       return;
     545             : 
     546             :     case eStateDisentangled:
     547           0 :       MOZ_CRASH("This cannot happen.");
     548             :       // It cannot happen because Disentangle should take off all the pending
     549             :       // messages.
     550             :       break;
     551             : 
     552             :     case eStateDisentangledForClose:
     553             :       // If we are here is because the port has been closed. We can still
     554             :       // process the pending messages.
     555           0 :       break;
     556             :   }
     557             : 
     558           0 :   RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
     559           0 :   mMessages.RemoveElementAt(0);
     560             : 
     561           0 :   mPostMessageRunnable = new PostMessageRunnable(this, data);
     562             : 
     563           0 :   nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
     564           0 :   if (NS_IsMainThread() && global) {
     565           0 :     MOZ_ALWAYS_SUCCEEDS(global->Dispatch("MessagePortMessage", TaskCategory::Other, do_AddRef(mPostMessageRunnable)));
     566           0 :     return;
     567             :   }
     568             : 
     569           0 :   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
     570             : }
     571             : 
     572             : void
     573           0 : MessagePort::Close()
     574             : {
     575           0 :   CloseInternal(true /* aSoftly */);
     576           0 : }
     577             : 
     578             : void
     579           0 : MessagePort::CloseForced()
     580             : {
     581           0 :   CloseInternal(false /* aSoftly */);
     582           0 : }
     583             : 
     584             : void
     585           0 : MessagePort::CloseInternal(bool aSoftly)
     586             : {
     587             :   // If we have some messages to send but we don't want a 'soft' close, we have
     588             :   // to flush them now.
     589           0 :   if (!aSoftly) {
     590           0 :     mMessages.Clear();
     591             :   }
     592             : 
     593           0 :   if (mState == eStateUnshippedEntangled) {
     594           0 :     MOZ_ASSERT(mUnshippedEntangledPort);
     595             : 
     596             :     // This avoids loops.
     597           0 :     RefPtr<MessagePort> port = Move(mUnshippedEntangledPort);
     598           0 :     MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
     599             : 
     600           0 :     mState = eStateDisentangledForClose;
     601           0 :     port->CloseInternal(aSoftly);
     602             : 
     603           0 :     UpdateMustKeepAlive();
     604           0 :     return;
     605             :   }
     606             : 
     607             :   // Not entangled yet, we have to wait.
     608           0 :   if (mState == eStateEntangling) {
     609           0 :     mState = eStateEntanglingForClose;
     610           0 :     return;
     611             :   }
     612             : 
     613             :   // Not entangled but already cloned or closed
     614           0 :   if (mState == eStateEntanglingForDisentangle ||
     615           0 :       mState == eStateEntanglingForClose) {
     616           0 :     return;
     617             :   }
     618             : 
     619             :   // Maybe we were already closing the port but softly. In this case we call
     620             :   // UpdateMustKeepAlive() to consider the empty pending message queue.
     621           0 :   if (mState == eStateDisentangledForClose && !aSoftly) {
     622           0 :     UpdateMustKeepAlive();
     623           0 :     return;
     624             :   }
     625             : 
     626           0 :   if (mState > eStateEntangled) {
     627           0 :     return;
     628             :   }
     629             : 
     630             :   // We don't care about stopping the sending of messages because from now all
     631             :   // the incoming messages will be ignored.
     632           0 :   mState = eStateDisentangledForClose;
     633             : 
     634           0 :   MOZ_ASSERT(mActor);
     635             : 
     636           0 :   mActor->SendClose();
     637           0 :   mActor->SetPort(nullptr);
     638           0 :   mActor = nullptr;
     639             : 
     640           0 :   UpdateMustKeepAlive();
     641             : }
     642             : 
     643             : EventHandlerNonNull*
     644           0 : MessagePort::GetOnmessage()
     645             : {
     646           0 :   if (NS_IsMainThread()) {
     647           0 :     return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
     648             :   }
     649           0 :   return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
     650             : }
     651             : 
     652             : void
     653           0 : MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
     654             : {
     655           0 :   if (NS_IsMainThread()) {
     656           0 :     SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
     657             :   } else {
     658           0 :     SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
     659             :   }
     660             : 
     661             :   // When using onmessage, the call to start() is implied.
     662           0 :   Start();
     663           0 : }
     664             : 
     665             : // This method is called when the PMessagePortChild actor is entangled to
     666             : // another actor. It receives a list of messages to be dispatch. It can be that
     667             : // we were waiting for this entangling step in order to disentangle the port or
     668             : // to close it.
     669             : void
     670           0 : MessagePort::Entangled(nsTArray<ClonedMessageData>& aMessages)
     671             : {
     672           0 :   MOZ_ASSERT(mState == eStateEntangling ||
     673             :              mState == eStateEntanglingForDisentangle ||
     674             :              mState == eStateEntanglingForClose);
     675             : 
     676           0 :   State oldState = mState;
     677           0 :   mState = eStateEntangled;
     678             : 
     679             :   // If we have pending messages, these have to be sent.
     680           0 :   if (!mMessagesForTheOtherPort.IsEmpty()) {
     681             :     {
     682           0 :       nsTArray<ClonedMessageData> messages;
     683           0 :       SharedMessagePortMessage::FromSharedToMessagesChild(mActor,
     684             :                                                           mMessagesForTheOtherPort,
     685           0 :                                                           messages);
     686           0 :       mActor->SendPostMessages(messages);
     687             :     }
     688             :     // Because `messages` borrow the underlying JSStructuredCloneData buffers,
     689             :     // only clear after `messages` have gone out of scope.
     690           0 :     mMessagesForTheOtherPort.Clear();
     691             :   }
     692             : 
     693             :   // We must convert the messages into SharedMessagePortMessages to avoid leaks.
     694           0 :   FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
     695           0 :   if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
     696             :                                                                       data))) {
     697             :     // OOM, we cannot continue.
     698           0 :     return;
     699             :   }
     700             : 
     701             :   // If the next step is to close the port, we do it ignoring the received
     702             :   // messages.
     703           0 :   if (oldState == eStateEntanglingForClose) {
     704           0 :     CloseForced();
     705           0 :     return;
     706             :   }
     707             : 
     708           0 :   mMessages.AppendElements(data);
     709             : 
     710             :   // We were waiting for the entangling callback in order to disentangle this
     711             :   // port immediately after.
     712           0 :   if (oldState == eStateEntanglingForDisentangle) {
     713           0 :     StartDisentangling();
     714           0 :     return;
     715             :   }
     716             : 
     717           0 :   Dispatch();
     718             : }
     719             : 
     720             : void
     721           0 : MessagePort::StartDisentangling()
     722             : {
     723           0 :   MOZ_ASSERT(mActor);
     724           0 :   MOZ_ASSERT(mState == eStateEntangled);
     725             : 
     726           0 :   mState = eStateDisentangling;
     727             : 
     728             :   // Sending this message we communicate to the parent actor that we don't want
     729             :   // to receive any new messages. It is possible that a message has been
     730             :   // already sent but not received yet. So we have to collect all of them and
     731             :   // we send them in the SendDispatch() request.
     732           0 :   mActor->SendStopSendingData();
     733           0 : }
     734             : 
     735             : void
     736           0 : MessagePort::MessagesReceived(nsTArray<ClonedMessageData>& aMessages)
     737             : {
     738           0 :   MOZ_ASSERT(mState == eStateEntangled ||
     739             :              mState == eStateDisentangling ||
     740             :              // This last step can happen only if Close() has been called
     741             :              // manually. At this point SendClose() is sent but we can still
     742             :              // receive something until the Closing request is processed.
     743             :              mState == eStateDisentangledForClose);
     744           0 :   MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
     745             : 
     746           0 :   RemoveDocFromBFCache();
     747             : 
     748           0 :   FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
     749           0 :   if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
     750             :                                                                       data))) {
     751             :     // OOM, We cannot continue.
     752           0 :     return;
     753             :   }
     754             : 
     755           0 :   mMessages.AppendElements(data);
     756             : 
     757           0 :   if (mState == eStateEntangled) {
     758           0 :     Dispatch();
     759             :   }
     760             : }
     761             : 
     762             : void
     763           0 : MessagePort::StopSendingDataConfirmed()
     764             : {
     765           0 :   MOZ_ASSERT(mState == eStateDisentangling);
     766           0 :   MOZ_ASSERT(mActor);
     767             : 
     768           0 :   Disentangle();
     769           0 : }
     770             : 
     771             : void
     772           0 : MessagePort::Disentangle()
     773             : {
     774           0 :   MOZ_ASSERT(mState == eStateDisentangling);
     775           0 :   MOZ_ASSERT(mActor);
     776             : 
     777           0 :   mState = eStateDisentangled;
     778             : 
     779             :   {
     780           0 :     nsTArray<ClonedMessageData> messages;
     781           0 :     SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessages,
     782           0 :                                                         messages);
     783           0 :     mActor->SendDisentangle(messages);
     784             :   }
     785             :   // Only clear mMessages after the ClonedMessageData instances have gone out of
     786             :   // scope because they borrow mMessages' underlying JSStructuredCloneDatas.
     787           0 :   mMessages.Clear();
     788             : 
     789           0 :   mActor->SetPort(nullptr);
     790           0 :   mActor = nullptr;
     791             : 
     792           0 :   UpdateMustKeepAlive();
     793           0 : }
     794             : 
     795             : void
     796           0 : MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
     797             : {
     798           0 :   MOZ_ASSERT(mIdentifier);
     799             : 
     800             :   // We can clone a port that has already been transfered. In this case, on the
     801             :   // otherside will have a neutered port. Here we set neutered to true so that
     802             :   // we are safe in case a early return.
     803           0 :   aIdentifier.neutered() = true;
     804             : 
     805           0 :   if (mState > eStateEntangled) {
     806           0 :     return;
     807             :   }
     808             : 
     809             :   // We already have a 'next step'. We have to consider this port as already
     810             :   // cloned/closed/disentangled.
     811           0 :   if (mState == eStateEntanglingForDisentangle ||
     812           0 :       mState == eStateEntanglingForClose) {
     813           0 :     return;
     814             :   }
     815             : 
     816           0 :   aIdentifier.uuid() = mIdentifier->uuid();
     817           0 :   aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
     818           0 :   aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
     819           0 :   aIdentifier.neutered() = false;
     820             : 
     821             :   // We have to entangle first.
     822           0 :   if (mState == eStateUnshippedEntangled) {
     823           0 :     MOZ_ASSERT(mUnshippedEntangledPort);
     824           0 :     MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
     825             : 
     826             :     // Disconnect the entangled port and connect it to PBackground.
     827           0 :     mUnshippedEntangledPort->ConnectToPBackground();
     828           0 :     mUnshippedEntangledPort = nullptr;
     829             : 
     830             :     // In this case, we don't need to be connected to the PBackground service.
     831           0 :     if (mMessages.IsEmpty()) {
     832           0 :       aIdentifier.sequenceId() = mIdentifier->sequenceId();
     833             : 
     834           0 :       mState = eStateDisentangled;
     835           0 :       UpdateMustKeepAlive();
     836           0 :       return;
     837             :     }
     838             : 
     839             :     // Register this component to PBackground.
     840           0 :     ConnectToPBackground();
     841             : 
     842           0 :     mState = eStateEntanglingForDisentangle;
     843           0 :     return;
     844             :   }
     845             : 
     846             :   // Not entangled yet, we have to wait.
     847           0 :   if (mState == eStateEntangling) {
     848           0 :     mState = eStateEntanglingForDisentangle;
     849           0 :     return;
     850             :   }
     851             : 
     852           0 :   MOZ_ASSERT(mState == eStateEntangled);
     853           0 :   StartDisentangling();
     854             : }
     855             : 
     856             : void
     857           0 : MessagePort::Closed()
     858             : {
     859           0 :   if (mState >= eStateDisentangled) {
     860           0 :     return;
     861             :   }
     862             : 
     863           0 :   mState = eStateDisentangledForClose;
     864             : 
     865           0 :   if (mActor) {
     866           0 :     mActor->SetPort(nullptr);
     867           0 :     mActor = nullptr;
     868             :   }
     869             : 
     870           0 :   UpdateMustKeepAlive();
     871             : }
     872             : 
     873             : void
     874           0 : MessagePort::ConnectToPBackground()
     875             : {
     876           0 :   mState = eStateEntangling;
     877             : 
     878             :   PBackgroundChild* actor =
     879           0 :     mozilla::ipc::BackgroundChild::GetForCurrentThread();
     880           0 :   if (actor) {
     881           0 :     ActorCreated(actor);
     882             :   } else {
     883           0 :     if (NS_WARN_IF(
     884             :         !mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
     885           0 :       MOZ_CRASH();
     886             :     }
     887             :   }
     888           0 : }
     889             : 
     890             : void
     891           0 : MessagePort::ActorFailed()
     892             : {
     893           0 :   MOZ_CRASH("Failed to create a PBackgroundChild actor!");
     894             : }
     895             : 
     896             : void
     897           0 : MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
     898             : {
     899           0 :   MOZ_ASSERT(aActor);
     900           0 :   MOZ_ASSERT(!mActor);
     901           0 :   MOZ_ASSERT(mIdentifier);
     902           0 :   MOZ_ASSERT(mState == eStateEntangling ||
     903             :              mState == eStateEntanglingForDisentangle ||
     904             :              mState == eStateEntanglingForClose);
     905             : 
     906             :   PMessagePortChild* actor =
     907           0 :     aActor->SendPMessagePortConstructor(mIdentifier->uuid(),
     908           0 :                                         mIdentifier->destinationUuid(),
     909           0 :                                         mIdentifier->sequenceId());
     910             : 
     911           0 :   mActor = static_cast<MessagePortChild*>(actor);
     912           0 :   MOZ_ASSERT(mActor);
     913             : 
     914           0 :   mActor->SetPort(this);
     915           0 : }
     916             : 
     917             : void
     918           0 : MessagePort::UpdateMustKeepAlive()
     919             : {
     920           0 :   if (mState >= eStateDisentangled &&
     921           0 :       mMessages.IsEmpty() &&
     922           0 :       mIsKeptAlive) {
     923           0 :     mIsKeptAlive = false;
     924             : 
     925             :     // The DTOR of this WorkerHolder will release the worker for us.
     926           0 :     mWorkerHolder = nullptr;
     927             : 
     928           0 :     if (NS_IsMainThread()) {
     929             :       nsCOMPtr<nsIObserverService> obs =
     930           0 :         do_GetService("@mozilla.org/observer-service;1");
     931           0 :       if (obs) {
     932           0 :         obs->RemoveObserver(this, "inner-window-destroyed");
     933             :       }
     934             :     }
     935             : 
     936           0 :     Release();
     937           0 :     return;
     938             :   }
     939             : 
     940           0 :   if (mState < eStateDisentangled && !mIsKeptAlive) {
     941           0 :     mIsKeptAlive = true;
     942           0 :     AddRef();
     943             :   }
     944             : }
     945             : 
     946             : NS_IMETHODIMP
     947           0 : MessagePort::Observe(nsISupports* aSubject, const char* aTopic,
     948             :                      const char16_t* aData)
     949             : {
     950           0 :   MOZ_ASSERT(NS_IsMainThread());
     951             : 
     952           0 :   if (strcmp(aTopic, "inner-window-destroyed")) {
     953           0 :     return NS_OK;
     954             :   }
     955             : 
     956             :   // If the window id destroyed we have to release the reference that we are
     957             :   // keeping.
     958           0 :   if (!mIsKeptAlive) {
     959           0 :     return NS_OK;
     960             :   }
     961             : 
     962           0 :   nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
     963           0 :   NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
     964             : 
     965             :   uint64_t innerID;
     966           0 :   nsresult rv = wrapper->GetData(&innerID);
     967           0 :   NS_ENSURE_SUCCESS(rv, rv);
     968             : 
     969           0 :   if (innerID == mInnerID) {
     970           0 :     CloseForced();
     971             :   }
     972             : 
     973           0 :   return NS_OK;
     974             : }
     975             : 
     976             : void
     977           0 : MessagePort::RemoveDocFromBFCache()
     978             : {
     979           0 :   if (!NS_IsMainThread()) {
     980           0 :     return;
     981             :   }
     982             : 
     983           0 :   nsPIDOMWindowInner* window = GetOwner();
     984           0 :   if (!window) {
     985           0 :     return;
     986             :   }
     987             : 
     988           0 :   nsIDocument* doc = window->GetExtantDoc();
     989           0 :   if (!doc) {
     990           0 :     return;
     991             :   }
     992             : 
     993           0 :   nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
     994           0 :   if (!bfCacheEntry) {
     995           0 :     return;
     996             :   }
     997             : 
     998           0 :   bfCacheEntry->RemoveFromBFCacheSync();
     999             : }
    1000             : 
    1001             : /* static */ void
    1002           0 : MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier)
    1003             : {
    1004           0 :   ForceCloseHelper::ForceClose(aIdentifier);
    1005           0 : }
    1006             : 
    1007             : } // namespace dom
    1008             : } // namespace mozilla

Generated by: LCOV version 1.13