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