123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "MessagePortService.h"
- #include "MessagePortParent.h"
- #include "SharedMessagePortMessage.h"
- #include "mozilla/ipc/BackgroundParent.h"
- #include "mozilla/StaticPtr.h"
- #include "mozilla/Unused.h"
- #include "mozilla/WeakPtr.h"
- #include "nsTArray.h"
- using mozilla::ipc::AssertIsOnBackgroundThread;
- namespace mozilla {
- namespace dom {
- namespace {
- StaticRefPtr<MessagePortService> gInstance;
- void
- AssertIsInMainProcess()
- {
- MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
- }
- } // namespace
- class MessagePortService::MessagePortServiceData final
- {
- public:
- explicit MessagePortServiceData(const nsID& aDestinationUUID)
- : mDestinationUUID(aDestinationUUID)
- , mSequenceID(1)
- , mParent(nullptr)
- // By default we don't know the next parent.
- , mWaitingForNewParent(true)
- , mNextStepCloseAll(false)
- {
- MOZ_COUNT_CTOR(MessagePortServiceData);
- }
- MessagePortServiceData(const MessagePortServiceData& aOther) = delete;
- MessagePortServiceData& operator=(const MessagePortServiceData&) = delete;
- ~MessagePortServiceData()
- {
- MOZ_COUNT_DTOR(MessagePortServiceData);
- }
- nsID mDestinationUUID;
- uint32_t mSequenceID;
- MessagePortParent* mParent;
- struct NextParent
- {
- uint32_t mSequenceID;
- // MessagePortParent keeps the service alive, and we don't want a cycle.
- WeakPtr<MessagePortParent> mParent;
- };
- FallibleTArray<NextParent> mNextParents;
- FallibleTArray<RefPtr<SharedMessagePortMessage>> mMessages;
- bool mWaitingForNewParent;
- bool mNextStepCloseAll;
- };
- /* static */ MessagePortService*
- MessagePortService::Get()
- {
- AssertIsInMainProcess();
- AssertIsOnBackgroundThread();
- return gInstance;
- }
- /* static */ MessagePortService*
- MessagePortService::GetOrCreate()
- {
- AssertIsInMainProcess();
- AssertIsOnBackgroundThread();
- if (!gInstance) {
- gInstance = new MessagePortService();
- }
- return gInstance;
- }
- bool
- MessagePortService::RequestEntangling(MessagePortParent* aParent,
- const nsID& aDestinationUUID,
- const uint32_t& aSequenceID)
- {
- MOZ_ASSERT(aParent);
- MessagePortServiceData* data;
- // If we don't have a MessagePortServiceData, we must create 2 of them for
- // both ports.
- if (!mPorts.Get(aParent->ID(), &data)) {
- // Create the MessagePortServiceData for the destination.
- if (mPorts.Get(aDestinationUUID, nullptr)) {
- MOZ_ASSERT(false, "The creation of the 2 ports should be in sync.");
- return false;
- }
- data = new MessagePortServiceData(aParent->ID());
- mPorts.Put(aDestinationUUID, data);
- data = new MessagePortServiceData(aDestinationUUID);
- mPorts.Put(aParent->ID(), data);
- }
- // This is a security check.
- if (!data->mDestinationUUID.Equals(aDestinationUUID)) {
- MOZ_ASSERT(false, "DestinationUUIDs do not match!");
- CloseAll(aParent->ID());
- return false;
- }
- if (aSequenceID < data->mSequenceID) {
- MOZ_ASSERT(false, "Invalid sequence ID!");
- CloseAll(aParent->ID());
- return false;
- }
- if (aSequenceID == data->mSequenceID) {
- if (data->mParent) {
- MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
- CloseAll(aParent->ID());
- return false;
- }
- // We activate this port, sending all the messages.
- data->mParent = aParent;
- data->mWaitingForNewParent = false;
- FallibleTArray<MessagePortMessage> array;
- if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent,
- data->mMessages,
- array)) {
- CloseAll(aParent->ID());
- return false;
- }
- data->mMessages.Clear();
- // We can entangle the port.
- if (!aParent->Entangled(array)) {
- CloseAll(aParent->ID());
- return false;
- }
- // If we were waiting for this parent in order to close this channel, this
- // is the time to do it.
- if (data->mNextStepCloseAll) {
- CloseAll(aParent->ID());
- }
- return true;
- }
- // This new parent will be the next one when a Disentangle request is
- // received from the current parent.
- MessagePortServiceData::NextParent* nextParent =
- data->mNextParents.AppendElement(mozilla::fallible);
- if (!nextParent) {
- CloseAll(aParent->ID());
- return false;
- }
- nextParent->mSequenceID = aSequenceID;
- nextParent->mParent = aParent;
- return true;
- }
- bool
- MessagePortService::DisentanglePort(
- MessagePortParent* aParent,
- FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages)
- {
- MessagePortServiceData* data;
- if (!mPorts.Get(aParent->ID(), &data)) {
- MOZ_ASSERT(false, "Unknown MessagePortParent should not happen.");
- return false;
- }
- if (data->mParent != aParent) {
- MOZ_ASSERT(false, "DisentanglePort() should be called just from the correct parent.");
- return false;
- }
- // Let's put the messages in the correct order. |aMessages| contains the
- // unsent messages so they have to go first.
- if (!aMessages.AppendElements(data->mMessages, mozilla::fallible)) {
- return false;
- }
- data->mMessages.Clear();
- ++data->mSequenceID;
- // If we don't have a parent, we have to store the pending messages and wait.
- uint32_t index = 0;
- MessagePortParent* nextParent = nullptr;
- for (; index < data->mNextParents.Length(); ++index) {
- if (data->mNextParents[index].mSequenceID == data->mSequenceID) {
- nextParent = data->mNextParents[index].mParent;
- break;
- }
- }
- // We didn't find the parent.
- if (!nextParent) {
- data->mMessages.SwapElements(aMessages);
- data->mWaitingForNewParent = true;
- data->mParent = nullptr;
- return true;
- }
- data->mParent = nextParent;
- data->mNextParents.RemoveElementAt(index);
- FallibleTArray<MessagePortMessage> array;
- if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
- aMessages,
- array)) {
- return false;
- }
- Unused << data->mParent->Entangled(array);
- return true;
- }
- bool
- MessagePortService::ClosePort(MessagePortParent* aParent)
- {
- MessagePortServiceData* data;
- if (!mPorts.Get(aParent->ID(), &data)) {
- MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
- return false;
- }
- if (data->mParent != aParent) {
- MOZ_ASSERT(false, "ClosePort() should be called just from the correct parent.");
- return false;
- }
- if (!data->mNextParents.IsEmpty()) {
- MOZ_ASSERT(false, "ClosePort() should be called when there are not next parents.");
- return false;
- }
- // We don't want to send a message to this parent.
- data->mParent = nullptr;
- CloseAll(aParent->ID());
- return true;
- }
- void
- MessagePortService::CloseAll(const nsID& aUUID, bool aForced)
- {
- MessagePortServiceData* data;
- if (!mPorts.Get(aUUID, &data)) {
- MaybeShutdown();
- return;
- }
- if (data->mParent) {
- data->mParent->Close();
- }
- for (const MessagePortServiceData::NextParent& nextParent : data->mNextParents) {
- MessagePortParent* const parent = nextParent.mParent;
- if (parent) {
- parent->CloseAndDelete();
- }
- }
- data->mNextParents.Clear();
- nsID destinationUUID = data->mDestinationUUID;
- // If we have informations about the other port and that port has some
- // pending messages to deliver but the parent has not processed them yet,
- // because its entangling request didn't arrive yet), we cannot close this
- // channel.
- MessagePortServiceData* destinationData;
- if (!aForced &&
- mPorts.Get(destinationUUID, &destinationData) &&
- !destinationData->mMessages.IsEmpty() &&
- destinationData->mWaitingForNewParent) {
- MOZ_ASSERT(!destinationData->mNextStepCloseAll);
- destinationData->mNextStepCloseAll = true;
- return;
- }
- mPorts.Remove(aUUID);
- CloseAll(destinationUUID, aForced);
- // CloseAll calls itself recursively and it can happen that it deletes
- // itself. Before continuing we must check if we are still alive.
- if (!gInstance) {
- return;
- }
- #ifdef DEBUG
- for (auto iter = mPorts.Iter(); !iter.Done(); iter.Next()) {
- MOZ_ASSERT(!aUUID.Equals(iter.Key()));
- }
- #endif
- MaybeShutdown();
- }
- // This service can be dismissed when there are not active ports.
- void
- MessagePortService::MaybeShutdown()
- {
- if (mPorts.Count() == 0) {
- gInstance = nullptr;
- }
- }
- bool
- MessagePortService::PostMessages(
- MessagePortParent* aParent,
- FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages)
- {
- MessagePortServiceData* data;
- if (!mPorts.Get(aParent->ID(), &data)) {
- MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
- return false;
- }
- if (data->mParent != aParent) {
- MOZ_ASSERT(false, "PostMessages() should be called just from the correct parent.");
- return false;
- }
- MOZ_ALWAYS_TRUE(mPorts.Get(data->mDestinationUUID, &data));
- if (!data->mMessages.AppendElements(aMessages, mozilla::fallible)) {
- return false;
- }
- // If the parent can send data to the child, let's proceed.
- if (data->mParent && data->mParent->CanSendData()) {
- FallibleTArray<MessagePortMessage> messages;
- if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
- data->mMessages,
- messages)) {
- return false;
- }
- data->mMessages.Clear();
- Unused << data->mParent->SendReceiveData(messages);
- }
- return true;
- }
- void
- MessagePortService::ParentDestroy(MessagePortParent* aParent)
- {
- // This port has already been destroyed.
- MessagePortServiceData* data;
- if (!mPorts.Get(aParent->ID(), &data)) {
- return;
- }
- if (data->mParent != aParent) {
- // We don't want to send a message to this parent.
- for (uint32_t i = 0; i < data->mNextParents.Length(); ++i) {
- if (aParent == data->mNextParents[i].mParent) {
- data->mNextParents.RemoveElementAt(i);
- break;
- }
- }
- }
- CloseAll(aParent->ID());
- }
- bool
- MessagePortService::ForceClose(const nsID& aUUID,
- const nsID& aDestinationUUID,
- const uint32_t& aSequenceID)
- {
- MessagePortServiceData* data;
- if (!mPorts.Get(aUUID, &data)) {
- NS_WARNING("Unknown MessagePort in ForceClose()");
- return true;
- }
- if (!data->mDestinationUUID.Equals(aDestinationUUID) ||
- data->mSequenceID != aSequenceID) {
- NS_WARNING("DestinationUUID and/or sequenceID do not match.");
- return false;
- }
- CloseAll(aUUID, true);
- return true;
- }
- } // namespace dom
- } // namespace mozilla
|