LCOV - code coverage report
Current view: top level - dom/messagechannel - MessagePortService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 157 0.6 %
Date: 2017-07-14 16:53:18 Functions: 0 13 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 "MessagePortService.h"
       8             : #include "MessagePortParent.h"
       9             : #include "SharedMessagePortMessage.h"
      10             : #include "mozilla/ipc/BackgroundParent.h"
      11             : #include "mozilla/StaticPtr.h"
      12             : #include "mozilla/Unused.h"
      13             : #include "nsTArray.h"
      14             : 
      15             : using mozilla::ipc::AssertIsOnBackgroundThread;
      16             : 
      17             : namespace mozilla {
      18             : namespace dom {
      19             : 
      20             : namespace {
      21             : 
      22           3 : StaticRefPtr<MessagePortService> gInstance;
      23             : 
      24             : void
      25           0 : AssertIsInMainProcess()
      26             : {
      27           0 :   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
      28           0 : }
      29             : 
      30             : } // namespace
      31             : 
      32             : class MessagePortService::MessagePortServiceData final
      33             : {
      34             : public:
      35           0 :   explicit MessagePortServiceData(const nsID& aDestinationUUID)
      36           0 :     : mDestinationUUID(aDestinationUUID)
      37             :     , mSequenceID(1)
      38             :     , mParent(nullptr)
      39             :     // By default we don't know the next parent.
      40             :     , mWaitingForNewParent(true)
      41           0 :     , mNextStepCloseAll(false)
      42             :   {
      43           0 :     MOZ_COUNT_CTOR(MessagePortServiceData);
      44           0 :   }
      45             : 
      46             :   MessagePortServiceData(const MessagePortServiceData& aOther) = delete;
      47             :   MessagePortServiceData& operator=(const MessagePortServiceData&) = delete;
      48             : 
      49           0 :   ~MessagePortServiceData()
      50           0 :   {
      51           0 :     MOZ_COUNT_DTOR(MessagePortServiceData);
      52           0 :   }
      53             : 
      54             :   nsID mDestinationUUID;
      55             : 
      56             :   uint32_t mSequenceID;
      57             :   MessagePortParent* mParent;
      58             : 
      59             :   struct NextParent
      60             :   {
      61             :     uint32_t mSequenceID;
      62             :     // MessagePortParent keeps the service alive, and we don't want a cycle.
      63             :     MessagePortParent* mParent;
      64             :   };
      65             : 
      66             :   FallibleTArray<NextParent> mNextParents;
      67             :   FallibleTArray<RefPtr<SharedMessagePortMessage>> mMessages;
      68             : 
      69             :   bool mWaitingForNewParent;
      70             :   bool mNextStepCloseAll;
      71             : };
      72             : 
      73             : /* static */ MessagePortService*
      74           0 : MessagePortService::Get()
      75             : {
      76           0 :   AssertIsInMainProcess();
      77           0 :   AssertIsOnBackgroundThread();
      78             : 
      79           0 :   return gInstance;
      80             : }
      81             : 
      82             : /* static */ MessagePortService*
      83           0 : MessagePortService::GetOrCreate()
      84             : {
      85           0 :   AssertIsInMainProcess();
      86           0 :   AssertIsOnBackgroundThread();
      87             : 
      88           0 :   if (!gInstance) {
      89           0 :     gInstance = new MessagePortService();
      90             :   }
      91             : 
      92           0 :   return gInstance;
      93             : }
      94             : 
      95             : bool
      96           0 : MessagePortService::RequestEntangling(MessagePortParent* aParent,
      97             :                                       const nsID& aDestinationUUID,
      98             :                                       const uint32_t& aSequenceID)
      99             : {
     100           0 :   MOZ_ASSERT(aParent);
     101             :   MessagePortServiceData* data;
     102             : 
     103             :   // If we don't have a MessagePortServiceData, we must create 2 of them for
     104             :   // both ports.
     105           0 :   if (!mPorts.Get(aParent->ID(), &data)) {
     106             :     // Create the MessagePortServiceData for the destination.
     107           0 :     if (mPorts.Get(aDestinationUUID, nullptr)) {
     108           0 :       MOZ_ASSERT(false, "The creation of the 2 ports should be in sync.");
     109             :       return false;
     110             :     }
     111             : 
     112           0 :     data = new MessagePortServiceData(aParent->ID());
     113           0 :     mPorts.Put(aDestinationUUID, data);
     114             : 
     115           0 :     data = new MessagePortServiceData(aDestinationUUID);
     116           0 :     mPorts.Put(aParent->ID(), data);
     117             :   }
     118             : 
     119             :   // This is a security check.
     120           0 :   if (!data->mDestinationUUID.Equals(aDestinationUUID)) {
     121           0 :     MOZ_ASSERT(false, "DestinationUUIDs do not match!");
     122             :     CloseAll(aParent->ID());
     123             :     return false;
     124             :   }
     125             : 
     126           0 :   if (aSequenceID < data->mSequenceID) {
     127           0 :     MOZ_ASSERT(false, "Invalid sequence ID!");
     128             :     CloseAll(aParent->ID());
     129             :     return false;
     130             :   }
     131             : 
     132           0 :   if (aSequenceID == data->mSequenceID) {
     133           0 :     if (data->mParent) {
     134           0 :       MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
     135             :       CloseAll(aParent->ID());
     136             :       return false;
     137             :     }
     138             : 
     139             :     // We activate this port, sending all the messages.
     140           0 :     data->mParent = aParent;
     141           0 :     data->mWaitingForNewParent = false;
     142             : 
     143             :     // We want to ensure we clear data->mMessages even if we early return, while
     144             :     // also ensuring that its contents remain alive until after array's contents
     145             :     // are destroyed because of JSStructuredCloneData borrowing.  So we use
     146             :     // Move to initialize things swapped and do it before we declare `array` so
     147             :     // that reverse destruction order works for us.
     148             :     FallibleTArray<RefPtr<SharedMessagePortMessage>>
     149           0 :       messages(Move(data->mMessages));
     150           0 :     FallibleTArray<ClonedMessageData> array;
     151           0 :     if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent,
     152             :                                                               messages,
     153             :                                                               array)) {
     154           0 :       CloseAll(aParent->ID());
     155           0 :       return false;
     156             :     }
     157             : 
     158             :     // We can entangle the port.
     159           0 :     if (!aParent->Entangled(array)) {
     160           0 :       CloseAll(aParent->ID());
     161           0 :       return false;
     162             :     }
     163             : 
     164             :     // If we were waiting for this parent in order to close this channel, this
     165             :     // is the time to do it.
     166           0 :     if (data->mNextStepCloseAll) {
     167           0 :       CloseAll(aParent->ID());
     168             :     }
     169             : 
     170           0 :     return true;
     171             :   }
     172             : 
     173             :   // This new parent will be the next one when a Disentangle request is
     174             :   // received from the current parent.
     175             :   MessagePortServiceData::NextParent* nextParent =
     176           0 :     data->mNextParents.AppendElement(mozilla::fallible);
     177           0 :   if (!nextParent) {
     178           0 :     CloseAll(aParent->ID());
     179           0 :     return false;
     180             :   }
     181             : 
     182           0 :   nextParent->mSequenceID = aSequenceID;
     183           0 :   nextParent->mParent = aParent;
     184             : 
     185           0 :   return true;
     186             : }
     187             : 
     188             : bool
     189           0 : MessagePortService::DisentanglePort(
     190             :                   MessagePortParent* aParent,
     191             :                   FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages)
     192             : {
     193             :   MessagePortServiceData* data;
     194           0 :   if (!mPorts.Get(aParent->ID(), &data)) {
     195           0 :     MOZ_ASSERT(false, "Unknown MessagePortParent should not happen.");
     196             :     return false;
     197             :   }
     198             : 
     199           0 :   if (data->mParent != aParent) {
     200           0 :     MOZ_ASSERT(false, "DisentanglePort() should be called just from the correct parent.");
     201             :     return false;
     202             :   }
     203             : 
     204             :   // Let's put the messages in the correct order. |aMessages| contains the
     205             :   // unsent messages so they have to go first.
     206           0 :   if (!aMessages.AppendElements(data->mMessages, mozilla::fallible)) {
     207           0 :     return false;
     208             :   }
     209             : 
     210           0 :   data->mMessages.Clear();
     211             : 
     212           0 :   ++data->mSequenceID;
     213             : 
     214             :   // If we don't have a parent, we have to store the pending messages and wait.
     215           0 :   uint32_t index = 0;
     216           0 :   MessagePortParent* nextParent = nullptr;
     217           0 :   for (; index < data->mNextParents.Length(); ++index) {
     218           0 :     if (data->mNextParents[index].mSequenceID == data->mSequenceID) {
     219           0 :       nextParent = data->mNextParents[index].mParent;
     220           0 :       break;
     221             :     }
     222             :   }
     223             : 
     224             :   // We didn't find the parent.
     225           0 :   if (!nextParent) {
     226           0 :     data->mMessages.SwapElements(aMessages);
     227           0 :     data->mWaitingForNewParent = true;
     228           0 :     data->mParent = nullptr;
     229           0 :     return true;
     230             :   }
     231             : 
     232           0 :   data->mParent = nextParent;
     233           0 :   data->mNextParents.RemoveElementAt(index);
     234             : 
     235           0 :   FallibleTArray<ClonedMessageData> array;
     236           0 :   if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
     237             :                                                             aMessages,
     238             :                                                             array)) {
     239           0 :     return false;
     240             :   }
     241             : 
     242           0 :   Unused << data->mParent->Entangled(array);
     243           0 :   return true;
     244             : }
     245             : 
     246             : bool
     247           0 : MessagePortService::ClosePort(MessagePortParent* aParent)
     248             : {
     249             :   MessagePortServiceData* data;
     250           0 :   if (!mPorts.Get(aParent->ID(), &data)) {
     251           0 :     MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
     252             :     return false;
     253             :   }
     254             : 
     255           0 :   if (data->mParent != aParent) {
     256           0 :     MOZ_ASSERT(false, "ClosePort() should be called just from the correct parent.");
     257             :     return false;
     258             :   }
     259             : 
     260           0 :   if (!data->mNextParents.IsEmpty()) {
     261           0 :     MOZ_ASSERT(false, "ClosePort() should be called when there are not next parents.");
     262             :     return false;
     263             :   }
     264             : 
     265             :   // We don't want to send a message to this parent.
     266           0 :   data->mParent = nullptr;
     267             : 
     268           0 :   CloseAll(aParent->ID());
     269           0 :   return true;
     270             : }
     271             : 
     272             : void
     273           0 : MessagePortService::CloseAll(const nsID& aUUID, bool aForced)
     274             : {
     275             :   MessagePortServiceData* data;
     276           0 :   if (!mPorts.Get(aUUID, &data)) {
     277           0 :     MaybeShutdown();
     278           0 :     return;
     279             :   }
     280             : 
     281           0 :   if (data->mParent) {
     282           0 :     data->mParent->Close();
     283             :   }
     284             : 
     285           0 :   for (const MessagePortServiceData::NextParent& parent : data->mNextParents) {
     286           0 :     parent.mParent->CloseAndDelete();
     287             :   }
     288             : 
     289           0 :   nsID destinationUUID = data->mDestinationUUID;
     290             : 
     291             :   // If we have informations about the other port and that port has some
     292             :   // pending messages to deliver but the parent has not processed them yet,
     293             :   // because its entangling request didn't arrive yet), we cannot close this
     294             :   // channel.
     295             :   MessagePortServiceData* destinationData;
     296           0 :   if (!aForced &&
     297           0 :       mPorts.Get(destinationUUID, &destinationData) &&
     298           0 :       !destinationData->mMessages.IsEmpty() &&
     299           0 :       destinationData->mWaitingForNewParent) {
     300           0 :     MOZ_ASSERT(!destinationData->mNextStepCloseAll);
     301           0 :     destinationData->mNextStepCloseAll = true;
     302           0 :     return;
     303             :   }
     304             : 
     305           0 :   mPorts.Remove(aUUID);
     306             : 
     307           0 :   CloseAll(destinationUUID, aForced);
     308             : 
     309             :   // CloseAll calls itself recursively and it can happen that it deletes
     310             :   // itself. Before continuing we must check if we are still alive.
     311           0 :   if (!gInstance) {
     312           0 :     return;
     313             :   }
     314             : 
     315             : #ifdef DEBUG
     316           0 :   for (auto iter = mPorts.Iter(); !iter.Done(); iter.Next()) {
     317           0 :     MOZ_ASSERT(!aUUID.Equals(iter.Key()));
     318             :   }
     319             : #endif
     320             : 
     321           0 :   MaybeShutdown();
     322             : }
     323             : 
     324             : // This service can be dismissed when there are not active ports.
     325             : void
     326           0 : MessagePortService::MaybeShutdown()
     327             : {
     328           0 :   if (mPorts.Count() == 0) {
     329           0 :     gInstance = nullptr;
     330             :   }
     331           0 : }
     332             : 
     333             : bool
     334           0 : MessagePortService::PostMessages(
     335             :                   MessagePortParent* aParent,
     336             :                   FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages)
     337             : {
     338             :   MessagePortServiceData* data;
     339           0 :   if (!mPorts.Get(aParent->ID(), &data)) {
     340           0 :     MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
     341             :     return false;
     342             :   }
     343             : 
     344           0 :   if (data->mParent != aParent) {
     345           0 :     MOZ_ASSERT(false, "PostMessages() should be called just from the correct parent.");
     346             :     return false;
     347             :   }
     348             : 
     349           0 :   MOZ_ALWAYS_TRUE(mPorts.Get(data->mDestinationUUID, &data));
     350             : 
     351           0 :   if (!data->mMessages.AppendElements(aMessages, mozilla::fallible)) {
     352           0 :     return false;
     353             :   }
     354             : 
     355             :   // If the parent can send data to the child, let's proceed.
     356           0 :   if (data->mParent && data->mParent->CanSendData()) {
     357             :     {
     358           0 :       FallibleTArray<ClonedMessageData> messages;
     359           0 :       if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
     360             :                                                                 data->mMessages,
     361             :                                                                 messages)) {
     362           0 :         return false;
     363             :       }
     364             : 
     365           0 :       Unused << data->mParent->SendReceiveData(messages);
     366             :     }
     367             :     // `messages` borrows the underlying JSStructuredCloneData so we need to
     368             :     // avoid destroying the `mMessages` until after we've destroyed `messages`.
     369           0 :     data->mMessages.Clear();
     370             :   }
     371             : 
     372           0 :   return true;
     373             : }
     374             : 
     375             : void
     376           0 : MessagePortService::ParentDestroy(MessagePortParent* aParent)
     377             : {
     378             :   // This port has already been destroyed.
     379             :   MessagePortServiceData* data;
     380           0 :   if (!mPorts.Get(aParent->ID(), &data)) {
     381           0 :     return;
     382             :   }
     383             : 
     384           0 :   if (data->mParent != aParent) {
     385             :     // We don't want to send a message to this parent.
     386           0 :     for (uint32_t i = 0; i < data->mNextParents.Length(); ++i) {
     387           0 :       if (aParent == data->mNextParents[i].mParent) {
     388           0 :        data->mNextParents.RemoveElementAt(i);
     389           0 :        break;
     390             :       }
     391             :     }
     392             :   }
     393             : 
     394           0 :   CloseAll(aParent->ID());
     395             : }
     396             : 
     397             : bool
     398           0 : MessagePortService::ForceClose(const nsID& aUUID,
     399             :                                const nsID& aDestinationUUID,
     400             :                                const uint32_t& aSequenceID)
     401             : {
     402             :   MessagePortServiceData* data;
     403           0 :   if (!mPorts.Get(aUUID, &data)) {
     404           0 :     NS_WARNING("Unknown MessagePort in ForceClose()");
     405           0 :     return true;
     406             :   }
     407             : 
     408           0 :   if (!data->mDestinationUUID.Equals(aDestinationUUID) ||
     409           0 :       data->mSequenceID != aSequenceID) {
     410           0 :     NS_WARNING("DestinationUUID and/or sequenceID do not match.");
     411           0 :     return false;
     412             :   }
     413             : 
     414           0 :   CloseAll(aUUID, true);
     415           0 :   return true;
     416             : }
     417             : 
     418             : } // namespace dom
     419             : } // namespace mozilla

Generated by: LCOV version 1.13