/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "SharedMessageBody.h" #include "mozilla/dom/File.h" #include "mozilla/dom/MessagePort.h" #include "mozilla/dom/RefMessageBodyService.h" #include "mozilla/dom/PMessagePort.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundParent.h" #include "xpcpublic.h" namespace mozilla { using namespace ipc; namespace dom { SharedMessageBody::SharedMessageBody( StructuredCloneHolder::TransferringSupport aSupportsTransferring, const Maybe& aAgentClusterId) : mRefDataId(Nothing()), mSupportsTransferring(aSupportsTransferring), mAgentClusterId(aAgentClusterId) {} void SharedMessageBody::Write(JSContext* aCx, JS::Handle aValue, JS::Handle aTransfers, nsID& aPortID, RefMessageBodyService* aRefMessageBodyService, ErrorResult& aRv) { MOZ_ASSERT(!mCloneData && !mRefData); MOZ_ASSERT(aRefMessageBodyService); JS::CloneDataPolicy cloneDataPolicy; // During a writing, we don't know the destination, so we assume it is part of // the same agent cluster. cloneDataPolicy.allowIntraClusterClonableSharedObjects(); nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); MOZ_ASSERT(global); if (global->IsSharedMemoryAllowed()) { cloneDataPolicy.allowSharedMemoryObjects(); } mCloneData = MakeUnique( JS::StructuredCloneScope::UnknownDestination, mSupportsTransferring); mCloneData->Write(aCx, aValue, aTransfers, cloneDataPolicy, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } if (mCloneData->CloneScope() == JS::StructuredCloneScope::DifferentProcess) { return; } MOZ_ASSERT(mCloneData->CloneScope() == JS::StructuredCloneScope::SameProcess); RefPtr refData = new RefMessageBody(aPortID, std::move(mCloneData)); mRefDataId.emplace(aRefMessageBodyService->Register(refData.forget(), aRv)); } void SharedMessageBody::Read(JSContext* aCx, JS::MutableHandle aValue, RefMessageBodyService* aRefMessageBodyService, SharedMessageBody::ReadMethod aReadMethod, ErrorResult& aRv) { MOZ_ASSERT(aRefMessageBodyService); if (mCloneData) { // Use a default cloneDataPolicy here, because SharedArrayBuffers and WASM // are not supported. return mCloneData->Read(aCx, aValue, JS::CloneDataPolicy(), aRv); } JS::CloneDataPolicy cloneDataPolicy; nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); MOZ_ASSERT(global); // Clones within the same agent cluster are allowed to use shared array // buffers and WASM modules. if (mAgentClusterId.isSome()) { Maybe agentClusterId = global->GetAgentClusterId(); if (agentClusterId.isSome() && mAgentClusterId.value().Equals(agentClusterId.value())) { cloneDataPolicy.allowIntraClusterClonableSharedObjects(); } } if (global->IsSharedMemoryAllowed()) { cloneDataPolicy.allowSharedMemoryObjects(); } MOZ_ASSERT(!mRefData); MOZ_ASSERT(mRefDataId.isSome()); if (aReadMethod == SharedMessageBody::StealRefMessageBody) { mRefData = aRefMessageBodyService->Steal(mRefDataId.value()); } else { MOZ_ASSERT(aReadMethod == SharedMessageBody::KeepRefMessageBody); mRefData = aRefMessageBodyService->GetAndCount(mRefDataId.value()); } if (!mRefData) { aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return; } mRefData->Read(aCx, aValue, cloneDataPolicy, aRv); } bool SharedMessageBody::TakeTransferredPortsAsSequence( Sequence>& aPorts) { if (mCloneData) { return mCloneData->TakeTransferredPortsAsSequence(aPorts); } MOZ_ASSERT(mRefData); return mRefData->TakeTransferredPortsAsSequence(aPorts); } /* static */ void SharedMessageBody::FromSharedToMessageChild( mozilla::ipc::PBackgroundChild* aManager, SharedMessageBody* aData, MessageData& aMessage) { MOZ_ASSERT(aManager); MOZ_ASSERT(aData); aMessage.agentClusterId() = aData->mAgentClusterId; if (aData->mCloneData) { ClonedMessageData clonedData; aData->mCloneData->BuildClonedMessageData(clonedData); aMessage.data() = std::move(clonedData); return; } MOZ_ASSERT(aData->mRefDataId.isSome()); aMessage.data() = RefMessageData(aData->mRefDataId.value()); } /* static */ void SharedMessageBody::FromSharedToMessagesChild( PBackgroundChild* aManager, const nsTArray>& aData, nsTArray& aArray) { MOZ_ASSERT(aManager); MOZ_ASSERT(aArray.IsEmpty()); aArray.SetCapacity(aData.Length()); for (auto& data : aData) { MessageData* message = aArray.AppendElement(); FromSharedToMessageChild(aManager, data, *message); } } /* static */ already_AddRefed SharedMessageBody::FromMessageToSharedChild( MessageData& aMessage, StructuredCloneHolder::TransferringSupport aSupportsTransferring) { RefPtr data = new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); if (aMessage.data().type() == MessageDataType::TClonedMessageData) { data->mCloneData = MakeUnique( JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring); data->mCloneData->StealFromClonedMessageData( aMessage.data().get_ClonedMessageData()); } else { MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData); data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid()); } return data.forget(); } /* static */ already_AddRefed SharedMessageBody::FromMessageToSharedChild( const MessageData& aMessage, StructuredCloneHolder::TransferringSupport aSupportsTransferring) { RefPtr data = new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); if (aMessage.data().type() == MessageDataType::TClonedMessageData) { data->mCloneData = MakeUnique( JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring); data->mCloneData->BorrowFromClonedMessageData( aMessage.data().get_ClonedMessageData()); } else { MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData); data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid()); } return data.forget(); } /* static */ bool SharedMessageBody::FromMessagesToSharedChild( nsTArray& aArray, FallibleTArray>& aData, StructuredCloneHolder::TransferringSupport aSupportsTransferring) { MOZ_ASSERT(aData.IsEmpty()); if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) { return false; } for (auto& message : aArray) { RefPtr data = FromMessageToSharedChild(message, aSupportsTransferring); if (!data || !aData.AppendElement(data, mozilla::fallible)) { return false; } } return true; } /* static */ bool SharedMessageBody::FromSharedToMessagesParent( PBackgroundParent* aManager, const nsTArray>& aData, nsTArray& aArray) { MOZ_ASSERT(aManager); MOZ_ASSERT(aArray.IsEmpty()); if (NS_WARN_IF(!aArray.SetCapacity(aData.Length(), mozilla::fallible))) { return false; } for (auto& data : aData) { MessageData* message = aArray.AppendElement(); message->agentClusterId() = data->mAgentClusterId; if (data->mCloneData) { ClonedMessageData clonedData; data->mCloneData->BuildClonedMessageData(clonedData); message->data() = std::move(clonedData); continue; } MOZ_ASSERT(data->mRefDataId.isSome()); message->data() = RefMessageData(data->mRefDataId.value()); } return true; } /* static */ already_AddRefed SharedMessageBody::FromMessageToSharedParent( MessageData& aMessage, StructuredCloneHolder::TransferringSupport aSupportsTransferring) { // TODO: This alloc is not fallible and there is no codepath that returns // nullptr. But the caller checks for nullptr and handles array allocations // for these items as fallible. See bug 1750497. RefPtr data = new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); if (aMessage.data().type() == MessageDataType::TClonedMessageData) { data->mCloneData = MakeUnique( JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring); data->mCloneData->StealFromClonedMessageData( aMessage.data().get_ClonedMessageData()); } else { MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData); data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid()); } return data.forget(); } bool SharedMessageBody::FromMessagesToSharedParent( nsTArray& aArray, FallibleTArray>& aData, StructuredCloneHolder::TransferringSupport aSupportsTransferring) { MOZ_ASSERT(aData.IsEmpty()); if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) { return false; } for (auto& message : aArray) { RefPtr data = FromMessageToSharedParent(message); if (!data || !aData.AppendElement(data, mozilla::fallible)) { return false; } } return true; } } // namespace dom } // namespace mozilla