SharedMessageBody.cpp (9869B)
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 "SharedMessageBody.h" 8 9 #include "mozilla/dom/File.h" 10 #include "mozilla/dom/MessagePort.h" 11 #include "mozilla/dom/PMessagePort.h" 12 #include "mozilla/dom/RefMessageBodyService.h" 13 #include "mozilla/ipc/BackgroundChild.h" 14 #include "mozilla/ipc/BackgroundParent.h" 15 #include "xpcpublic.h" 16 17 namespace mozilla { 18 19 using namespace ipc; 20 21 namespace dom { 22 23 SharedMessageBody::SharedMessageBody( 24 StructuredCloneHolder::TransferringSupport aSupportsTransferring, 25 const Maybe<nsID>& aAgentClusterId) 26 : mRefDataId(Nothing()), 27 mSupportsTransferring(aSupportsTransferring), 28 mAgentClusterId(aAgentClusterId) {} 29 30 void SharedMessageBody::Write(JSContext* aCx, JS::Handle<JS::Value> aValue, 31 JS::Handle<JS::Value> aTransfers, nsID& aPortID, 32 RefMessageBodyService* aRefMessageBodyService, 33 ErrorResult& aRv) { 34 MOZ_ASSERT(!mCloneData && !mRefData); 35 MOZ_ASSERT(aRefMessageBodyService); 36 37 JS::CloneDataPolicy cloneDataPolicy; 38 // During a writing, we don't know the destination, so we assume it is part of 39 // the same agent cluster. 40 cloneDataPolicy.allowIntraClusterClonableSharedObjects(); 41 42 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 43 MOZ_ASSERT(global); 44 45 if (global->IsSharedMemoryAllowed()) { 46 cloneDataPolicy.allowSharedMemoryObjects(); 47 } 48 49 mCloneData = MakeUnique<ipc::StructuredCloneData>( 50 JS::StructuredCloneScope::UnknownDestination, mSupportsTransferring); 51 mCloneData->Write(aCx, aValue, aTransfers, cloneDataPolicy, aRv); 52 if (NS_WARN_IF(aRv.Failed())) { 53 return; 54 } 55 56 if (mCloneData->CloneScope() == JS::StructuredCloneScope::DifferentProcess) { 57 return; 58 } 59 60 MOZ_ASSERT(mCloneData->CloneScope() == JS::StructuredCloneScope::SameProcess); 61 RefPtr<RefMessageBody> refData = 62 new RefMessageBody(aPortID, std::move(mCloneData)); 63 64 mRefDataId.emplace(aRefMessageBodyService->Register(refData.forget(), aRv)); 65 } 66 67 void SharedMessageBody::Read(JSContext* aCx, 68 JS::MutableHandle<JS::Value> aValue, 69 RefMessageBodyService* aRefMessageBodyService, 70 SharedMessageBody::ReadMethod aReadMethod, 71 ErrorResult& aRv) { 72 MOZ_ASSERT(aRefMessageBodyService); 73 74 if (mCloneData) { 75 // Use a default cloneDataPolicy here, because SharedArrayBuffers and WASM 76 // are not supported. 77 return mCloneData->Read(aCx, aValue, JS::CloneDataPolicy(), aRv); 78 } 79 80 JS::CloneDataPolicy cloneDataPolicy; 81 82 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 83 MOZ_ASSERT(global); 84 85 // Clones within the same agent cluster are allowed to use shared array 86 // buffers and WASM modules. 87 if (mAgentClusterId.isSome()) { 88 Maybe<nsID> agentClusterId = global->GetAgentClusterId(); 89 if (agentClusterId.isSome() && 90 mAgentClusterId.value().Equals(agentClusterId.value())) { 91 cloneDataPolicy.allowIntraClusterClonableSharedObjects(); 92 } 93 } 94 95 if (global->IsSharedMemoryAllowed()) { 96 cloneDataPolicy.allowSharedMemoryObjects(); 97 } 98 99 MOZ_ASSERT(!mRefData); 100 MOZ_ASSERT(mRefDataId.isSome()); 101 102 if (aReadMethod == SharedMessageBody::StealRefMessageBody) { 103 mRefData = aRefMessageBodyService->Steal(mRefDataId.value()); 104 } else { 105 MOZ_ASSERT(aReadMethod == SharedMessageBody::KeepRefMessageBody); 106 mRefData = aRefMessageBodyService->GetAndCount(mRefDataId.value()); 107 } 108 109 if (!mRefData) { 110 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); 111 return; 112 } 113 114 mRefData->Read(aCx, aValue, cloneDataPolicy, aRv); 115 } 116 117 bool SharedMessageBody::TakeTransferredPortsAsSequence( 118 Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts) { 119 if (mCloneData) { 120 return mCloneData->TakeTransferredPortsAsSequence(aPorts); 121 } 122 123 MOZ_ASSERT(mRefData); 124 return mRefData->TakeTransferredPortsAsSequence(aPorts); 125 } 126 127 /* static */ 128 void SharedMessageBody::FromSharedToMessageChild( 129 mozilla::ipc::PBackgroundChild* aManager, SharedMessageBody* aData, 130 MessageData& aMessage) { 131 MOZ_ASSERT(aManager); 132 MOZ_ASSERT(aData); 133 134 aMessage.agentClusterId() = aData->mAgentClusterId; 135 136 if (aData->mCloneData) { 137 ClonedMessageData clonedData; 138 aData->mCloneData->BuildClonedMessageData(clonedData); 139 aMessage.data() = std::move(clonedData); 140 return; 141 } 142 143 MOZ_ASSERT(aData->mRefDataId.isSome()); 144 aMessage.data() = RefMessageData(aData->mRefDataId.value()); 145 } 146 147 /* static */ 148 void SharedMessageBody::FromSharedToMessagesChild( 149 PBackgroundChild* aManager, 150 const nsTArray<RefPtr<SharedMessageBody>>& aData, 151 nsTArray<MessageData>& aArray) { 152 MOZ_ASSERT(aManager); 153 MOZ_ASSERT(aArray.IsEmpty()); 154 aArray.SetCapacity(aData.Length()); 155 156 for (auto& data : aData) { 157 MessageData* message = aArray.AppendElement(); 158 FromSharedToMessageChild(aManager, data, *message); 159 } 160 } 161 162 /* static */ 163 already_AddRefed<SharedMessageBody> SharedMessageBody::FromMessageToSharedChild( 164 MessageData& aMessage, 165 StructuredCloneHolder::TransferringSupport aSupportsTransferring) { 166 RefPtr<SharedMessageBody> data = 167 new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); 168 169 if (aMessage.data().type() == MessageDataType::TClonedMessageData) { 170 data->mCloneData = MakeUnique<ipc::StructuredCloneData>( 171 JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring); 172 data->mCloneData->StealFromClonedMessageData( 173 aMessage.data().get_ClonedMessageData()); 174 } else { 175 MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData); 176 data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid()); 177 } 178 179 return data.forget(); 180 } 181 182 /* static */ 183 already_AddRefed<SharedMessageBody> SharedMessageBody::FromMessageToSharedChild( 184 const MessageData& aMessage, 185 StructuredCloneHolder::TransferringSupport aSupportsTransferring) { 186 RefPtr<SharedMessageBody> data = 187 new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); 188 189 if (aMessage.data().type() == MessageDataType::TClonedMessageData) { 190 data->mCloneData = MakeUnique<ipc::StructuredCloneData>( 191 JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring); 192 data->mCloneData->BorrowFromClonedMessageData( 193 aMessage.data().get_ClonedMessageData()); 194 } else { 195 MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData); 196 data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid()); 197 } 198 199 return data.forget(); 200 } 201 202 /* static */ 203 bool SharedMessageBody::FromMessagesToSharedChild( 204 nsTArray<MessageData>& aArray, 205 FallibleTArray<RefPtr<SharedMessageBody>>& aData, 206 StructuredCloneHolder::TransferringSupport aSupportsTransferring) { 207 MOZ_ASSERT(aData.IsEmpty()); 208 209 if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) { 210 return false; 211 } 212 213 for (auto& message : aArray) { 214 RefPtr<SharedMessageBody> data = 215 FromMessageToSharedChild(message, aSupportsTransferring); 216 if (!data || !aData.AppendElement(data, mozilla::fallible)) { 217 return false; 218 } 219 } 220 221 return true; 222 } 223 224 /* static */ 225 bool SharedMessageBody::FromSharedToMessagesParent( 226 PBackgroundParent* aManager, 227 const nsTArray<RefPtr<SharedMessageBody>>& aData, 228 nsTArray<MessageData>& aArray) { 229 MOZ_ASSERT(aManager); 230 MOZ_ASSERT(aArray.IsEmpty()); 231 232 if (NS_WARN_IF(!aArray.SetCapacity(aData.Length(), mozilla::fallible))) { 233 return false; 234 } 235 236 for (auto& data : aData) { 237 MessageData* message = aArray.AppendElement(); 238 message->agentClusterId() = data->mAgentClusterId; 239 240 if (data->mCloneData) { 241 ClonedMessageData clonedData; 242 data->mCloneData->BuildClonedMessageData(clonedData); 243 message->data() = std::move(clonedData); 244 continue; 245 } 246 247 MOZ_ASSERT(data->mRefDataId.isSome()); 248 message->data() = RefMessageData(data->mRefDataId.value()); 249 } 250 251 return true; 252 } 253 254 /* static */ 255 already_AddRefed<SharedMessageBody> 256 SharedMessageBody::FromMessageToSharedParent( 257 MessageData& aMessage, 258 StructuredCloneHolder::TransferringSupport aSupportsTransferring) { 259 // TODO: This alloc is not fallible and there is no codepath that returns 260 // nullptr. But the caller checks for nullptr and handles array allocations 261 // for these items as fallible. See bug 1750497. 262 RefPtr<SharedMessageBody> data = 263 new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId()); 264 265 if (aMessage.data().type() == MessageDataType::TClonedMessageData) { 266 data->mCloneData = MakeUnique<ipc::StructuredCloneData>( 267 JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring); 268 data->mCloneData->StealFromClonedMessageData( 269 aMessage.data().get_ClonedMessageData()); 270 } else { 271 MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData); 272 data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid()); 273 } 274 275 return data.forget(); 276 } 277 278 bool SharedMessageBody::FromMessagesToSharedParent( 279 nsTArray<MessageData>& aArray, 280 FallibleTArray<RefPtr<SharedMessageBody>>& aData, 281 StructuredCloneHolder::TransferringSupport aSupportsTransferring) { 282 MOZ_ASSERT(aData.IsEmpty()); 283 284 if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) { 285 return false; 286 } 287 288 for (auto& message : aArray) { 289 RefPtr<SharedMessageBody> data = FromMessageToSharedParent(message); 290 if (!data || !aData.AppendElement(data, mozilla::fallible)) { 291 return false; 292 } 293 } 294 295 return true; 296 } 297 298 } // namespace dom 299 } // namespace mozilla