diff options
Diffstat (limited to '')
-rw-r--r-- | dom/ipc/SharedMessageBody.cpp | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/dom/ipc/SharedMessageBody.cpp b/dom/ipc/SharedMessageBody.cpp new file mode 100644 index 0000000000..e09ff2fd55 --- /dev/null +++ b/dom/ipc/SharedMessageBody.cpp @@ -0,0 +1,298 @@ +/* -*- 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<nsID>& aAgentClusterId) + : mRefDataId(Nothing()), + mSupportsTransferring(aSupportsTransferring), + mAgentClusterId(aAgentClusterId) {} + +void SharedMessageBody::Write(JSContext* aCx, JS::Handle<JS::Value> aValue, + JS::Handle<JS::Value> 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<ipc::StructuredCloneData>( + 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<RefMessageBody> refData = + new RefMessageBody(aPortID, std::move(mCloneData)); + + mRefDataId.emplace(aRefMessageBodyService->Register(refData.forget(), aRv)); +} + +void SharedMessageBody::Read(JSContext* aCx, + JS::MutableHandle<JS::Value> 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<nsID> 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<OwningNonNull<mozilla::dom::MessagePort>>& 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<RefPtr<SharedMessageBody>>& aData, + nsTArray<MessageData>& 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> SharedMessageBody::FromMessageToSharedChild( + MessageData& aMessage, + StructuredCloneHolder::TransferringSupport aSupportsTransferring) { + RefPtr<SharedMessageBody> data = + new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); + + if (aMessage.data().type() == MessageDataType::TClonedMessageData) { + data->mCloneData = MakeUnique<ipc::StructuredCloneData>( + 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> SharedMessageBody::FromMessageToSharedChild( + const MessageData& aMessage, + StructuredCloneHolder::TransferringSupport aSupportsTransferring) { + RefPtr<SharedMessageBody> data = + new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); + + if (aMessage.data().type() == MessageDataType::TClonedMessageData) { + data->mCloneData = MakeUnique<ipc::StructuredCloneData>( + 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<MessageData>& aArray, + FallibleTArray<RefPtr<SharedMessageBody>>& 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<SharedMessageBody> data = + FromMessageToSharedChild(message, aSupportsTransferring); + if (!data || !aData.AppendElement(data, mozilla::fallible)) { + return false; + } + } + + return true; +} + +/* static */ +bool SharedMessageBody::FromSharedToMessagesParent( + PBackgroundParent* aManager, + const nsTArray<RefPtr<SharedMessageBody>>& aData, + nsTArray<MessageData>& 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> +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<SharedMessageBody> data = + new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); + + if (aMessage.data().type() == MessageDataType::TClonedMessageData) { + data->mCloneData = MakeUnique<ipc::StructuredCloneData>( + 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<MessageData>& aArray, + FallibleTArray<RefPtr<SharedMessageBody>>& 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<SharedMessageBody> data = FromMessageToSharedParent(message); + if (!data || !aData.AppendElement(data, mozilla::fallible)) { + return false; + } + } + + return true; +} + +} // namespace dom +} // namespace mozilla |