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