Line data Source code
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 "MessagePortService.h"
8 : #include "MessagePortParent.h"
9 : #include "SharedMessagePortMessage.h"
10 : #include "mozilla/ipc/BackgroundParent.h"
11 : #include "mozilla/StaticPtr.h"
12 : #include "mozilla/Unused.h"
13 : #include "nsTArray.h"
14 :
15 : using mozilla::ipc::AssertIsOnBackgroundThread;
16 :
17 : namespace mozilla {
18 : namespace dom {
19 :
20 : namespace {
21 :
22 3 : StaticRefPtr<MessagePortService> gInstance;
23 :
24 : void
25 0 : AssertIsInMainProcess()
26 : {
27 0 : MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
28 0 : }
29 :
30 : } // namespace
31 :
32 : class MessagePortService::MessagePortServiceData final
33 : {
34 : public:
35 0 : explicit MessagePortServiceData(const nsID& aDestinationUUID)
36 0 : : mDestinationUUID(aDestinationUUID)
37 : , mSequenceID(1)
38 : , mParent(nullptr)
39 : // By default we don't know the next parent.
40 : , mWaitingForNewParent(true)
41 0 : , mNextStepCloseAll(false)
42 : {
43 0 : MOZ_COUNT_CTOR(MessagePortServiceData);
44 0 : }
45 :
46 : MessagePortServiceData(const MessagePortServiceData& aOther) = delete;
47 : MessagePortServiceData& operator=(const MessagePortServiceData&) = delete;
48 :
49 0 : ~MessagePortServiceData()
50 0 : {
51 0 : MOZ_COUNT_DTOR(MessagePortServiceData);
52 0 : }
53 :
54 : nsID mDestinationUUID;
55 :
56 : uint32_t mSequenceID;
57 : MessagePortParent* mParent;
58 :
59 : struct NextParent
60 : {
61 : uint32_t mSequenceID;
62 : // MessagePortParent keeps the service alive, and we don't want a cycle.
63 : MessagePortParent* mParent;
64 : };
65 :
66 : FallibleTArray<NextParent> mNextParents;
67 : FallibleTArray<RefPtr<SharedMessagePortMessage>> mMessages;
68 :
69 : bool mWaitingForNewParent;
70 : bool mNextStepCloseAll;
71 : };
72 :
73 : /* static */ MessagePortService*
74 0 : MessagePortService::Get()
75 : {
76 0 : AssertIsInMainProcess();
77 0 : AssertIsOnBackgroundThread();
78 :
79 0 : return gInstance;
80 : }
81 :
82 : /* static */ MessagePortService*
83 0 : MessagePortService::GetOrCreate()
84 : {
85 0 : AssertIsInMainProcess();
86 0 : AssertIsOnBackgroundThread();
87 :
88 0 : if (!gInstance) {
89 0 : gInstance = new MessagePortService();
90 : }
91 :
92 0 : return gInstance;
93 : }
94 :
95 : bool
96 0 : MessagePortService::RequestEntangling(MessagePortParent* aParent,
97 : const nsID& aDestinationUUID,
98 : const uint32_t& aSequenceID)
99 : {
100 0 : MOZ_ASSERT(aParent);
101 : MessagePortServiceData* data;
102 :
103 : // If we don't have a MessagePortServiceData, we must create 2 of them for
104 : // both ports.
105 0 : if (!mPorts.Get(aParent->ID(), &data)) {
106 : // Create the MessagePortServiceData for the destination.
107 0 : if (mPorts.Get(aDestinationUUID, nullptr)) {
108 0 : MOZ_ASSERT(false, "The creation of the 2 ports should be in sync.");
109 : return false;
110 : }
111 :
112 0 : data = new MessagePortServiceData(aParent->ID());
113 0 : mPorts.Put(aDestinationUUID, data);
114 :
115 0 : data = new MessagePortServiceData(aDestinationUUID);
116 0 : mPorts.Put(aParent->ID(), data);
117 : }
118 :
119 : // This is a security check.
120 0 : if (!data->mDestinationUUID.Equals(aDestinationUUID)) {
121 0 : MOZ_ASSERT(false, "DestinationUUIDs do not match!");
122 : CloseAll(aParent->ID());
123 : return false;
124 : }
125 :
126 0 : if (aSequenceID < data->mSequenceID) {
127 0 : MOZ_ASSERT(false, "Invalid sequence ID!");
128 : CloseAll(aParent->ID());
129 : return false;
130 : }
131 :
132 0 : if (aSequenceID == data->mSequenceID) {
133 0 : if (data->mParent) {
134 0 : MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
135 : CloseAll(aParent->ID());
136 : return false;
137 : }
138 :
139 : // We activate this port, sending all the messages.
140 0 : data->mParent = aParent;
141 0 : data->mWaitingForNewParent = false;
142 :
143 : // We want to ensure we clear data->mMessages even if we early return, while
144 : // also ensuring that its contents remain alive until after array's contents
145 : // are destroyed because of JSStructuredCloneData borrowing. So we use
146 : // Move to initialize things swapped and do it before we declare `array` so
147 : // that reverse destruction order works for us.
148 : FallibleTArray<RefPtr<SharedMessagePortMessage>>
149 0 : messages(Move(data->mMessages));
150 0 : FallibleTArray<ClonedMessageData> array;
151 0 : if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent,
152 : messages,
153 : array)) {
154 0 : CloseAll(aParent->ID());
155 0 : return false;
156 : }
157 :
158 : // We can entangle the port.
159 0 : if (!aParent->Entangled(array)) {
160 0 : CloseAll(aParent->ID());
161 0 : return false;
162 : }
163 :
164 : // If we were waiting for this parent in order to close this channel, this
165 : // is the time to do it.
166 0 : if (data->mNextStepCloseAll) {
167 0 : CloseAll(aParent->ID());
168 : }
169 :
170 0 : return true;
171 : }
172 :
173 : // This new parent will be the next one when a Disentangle request is
174 : // received from the current parent.
175 : MessagePortServiceData::NextParent* nextParent =
176 0 : data->mNextParents.AppendElement(mozilla::fallible);
177 0 : if (!nextParent) {
178 0 : CloseAll(aParent->ID());
179 0 : return false;
180 : }
181 :
182 0 : nextParent->mSequenceID = aSequenceID;
183 0 : nextParent->mParent = aParent;
184 :
185 0 : return true;
186 : }
187 :
188 : bool
189 0 : MessagePortService::DisentanglePort(
190 : MessagePortParent* aParent,
191 : FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages)
192 : {
193 : MessagePortServiceData* data;
194 0 : if (!mPorts.Get(aParent->ID(), &data)) {
195 0 : MOZ_ASSERT(false, "Unknown MessagePortParent should not happen.");
196 : return false;
197 : }
198 :
199 0 : if (data->mParent != aParent) {
200 0 : MOZ_ASSERT(false, "DisentanglePort() should be called just from the correct parent.");
201 : return false;
202 : }
203 :
204 : // Let's put the messages in the correct order. |aMessages| contains the
205 : // unsent messages so they have to go first.
206 0 : if (!aMessages.AppendElements(data->mMessages, mozilla::fallible)) {
207 0 : return false;
208 : }
209 :
210 0 : data->mMessages.Clear();
211 :
212 0 : ++data->mSequenceID;
213 :
214 : // If we don't have a parent, we have to store the pending messages and wait.
215 0 : uint32_t index = 0;
216 0 : MessagePortParent* nextParent = nullptr;
217 0 : for (; index < data->mNextParents.Length(); ++index) {
218 0 : if (data->mNextParents[index].mSequenceID == data->mSequenceID) {
219 0 : nextParent = data->mNextParents[index].mParent;
220 0 : break;
221 : }
222 : }
223 :
224 : // We didn't find the parent.
225 0 : if (!nextParent) {
226 0 : data->mMessages.SwapElements(aMessages);
227 0 : data->mWaitingForNewParent = true;
228 0 : data->mParent = nullptr;
229 0 : return true;
230 : }
231 :
232 0 : data->mParent = nextParent;
233 0 : data->mNextParents.RemoveElementAt(index);
234 :
235 0 : FallibleTArray<ClonedMessageData> array;
236 0 : if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
237 : aMessages,
238 : array)) {
239 0 : return false;
240 : }
241 :
242 0 : Unused << data->mParent->Entangled(array);
243 0 : return true;
244 : }
245 :
246 : bool
247 0 : MessagePortService::ClosePort(MessagePortParent* aParent)
248 : {
249 : MessagePortServiceData* data;
250 0 : if (!mPorts.Get(aParent->ID(), &data)) {
251 0 : MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
252 : return false;
253 : }
254 :
255 0 : if (data->mParent != aParent) {
256 0 : MOZ_ASSERT(false, "ClosePort() should be called just from the correct parent.");
257 : return false;
258 : }
259 :
260 0 : if (!data->mNextParents.IsEmpty()) {
261 0 : MOZ_ASSERT(false, "ClosePort() should be called when there are not next parents.");
262 : return false;
263 : }
264 :
265 : // We don't want to send a message to this parent.
266 0 : data->mParent = nullptr;
267 :
268 0 : CloseAll(aParent->ID());
269 0 : return true;
270 : }
271 :
272 : void
273 0 : MessagePortService::CloseAll(const nsID& aUUID, bool aForced)
274 : {
275 : MessagePortServiceData* data;
276 0 : if (!mPorts.Get(aUUID, &data)) {
277 0 : MaybeShutdown();
278 0 : return;
279 : }
280 :
281 0 : if (data->mParent) {
282 0 : data->mParent->Close();
283 : }
284 :
285 0 : for (const MessagePortServiceData::NextParent& parent : data->mNextParents) {
286 0 : parent.mParent->CloseAndDelete();
287 : }
288 :
289 0 : nsID destinationUUID = data->mDestinationUUID;
290 :
291 : // If we have informations about the other port and that port has some
292 : // pending messages to deliver but the parent has not processed them yet,
293 : // because its entangling request didn't arrive yet), we cannot close this
294 : // channel.
295 : MessagePortServiceData* destinationData;
296 0 : if (!aForced &&
297 0 : mPorts.Get(destinationUUID, &destinationData) &&
298 0 : !destinationData->mMessages.IsEmpty() &&
299 0 : destinationData->mWaitingForNewParent) {
300 0 : MOZ_ASSERT(!destinationData->mNextStepCloseAll);
301 0 : destinationData->mNextStepCloseAll = true;
302 0 : return;
303 : }
304 :
305 0 : mPorts.Remove(aUUID);
306 :
307 0 : CloseAll(destinationUUID, aForced);
308 :
309 : // CloseAll calls itself recursively and it can happen that it deletes
310 : // itself. Before continuing we must check if we are still alive.
311 0 : if (!gInstance) {
312 0 : return;
313 : }
314 :
315 : #ifdef DEBUG
316 0 : for (auto iter = mPorts.Iter(); !iter.Done(); iter.Next()) {
317 0 : MOZ_ASSERT(!aUUID.Equals(iter.Key()));
318 : }
319 : #endif
320 :
321 0 : MaybeShutdown();
322 : }
323 :
324 : // This service can be dismissed when there are not active ports.
325 : void
326 0 : MessagePortService::MaybeShutdown()
327 : {
328 0 : if (mPorts.Count() == 0) {
329 0 : gInstance = nullptr;
330 : }
331 0 : }
332 :
333 : bool
334 0 : MessagePortService::PostMessages(
335 : MessagePortParent* aParent,
336 : FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages)
337 : {
338 : MessagePortServiceData* data;
339 0 : if (!mPorts.Get(aParent->ID(), &data)) {
340 0 : MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
341 : return false;
342 : }
343 :
344 0 : if (data->mParent != aParent) {
345 0 : MOZ_ASSERT(false, "PostMessages() should be called just from the correct parent.");
346 : return false;
347 : }
348 :
349 0 : MOZ_ALWAYS_TRUE(mPorts.Get(data->mDestinationUUID, &data));
350 :
351 0 : if (!data->mMessages.AppendElements(aMessages, mozilla::fallible)) {
352 0 : return false;
353 : }
354 :
355 : // If the parent can send data to the child, let's proceed.
356 0 : if (data->mParent && data->mParent->CanSendData()) {
357 : {
358 0 : FallibleTArray<ClonedMessageData> messages;
359 0 : if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
360 : data->mMessages,
361 : messages)) {
362 0 : return false;
363 : }
364 :
365 0 : Unused << data->mParent->SendReceiveData(messages);
366 : }
367 : // `messages` borrows the underlying JSStructuredCloneData so we need to
368 : // avoid destroying the `mMessages` until after we've destroyed `messages`.
369 0 : data->mMessages.Clear();
370 : }
371 :
372 0 : return true;
373 : }
374 :
375 : void
376 0 : MessagePortService::ParentDestroy(MessagePortParent* aParent)
377 : {
378 : // This port has already been destroyed.
379 : MessagePortServiceData* data;
380 0 : if (!mPorts.Get(aParent->ID(), &data)) {
381 0 : return;
382 : }
383 :
384 0 : if (data->mParent != aParent) {
385 : // We don't want to send a message to this parent.
386 0 : for (uint32_t i = 0; i < data->mNextParents.Length(); ++i) {
387 0 : if (aParent == data->mNextParents[i].mParent) {
388 0 : data->mNextParents.RemoveElementAt(i);
389 0 : break;
390 : }
391 : }
392 : }
393 :
394 0 : CloseAll(aParent->ID());
395 : }
396 :
397 : bool
398 0 : MessagePortService::ForceClose(const nsID& aUUID,
399 : const nsID& aDestinationUUID,
400 : const uint32_t& aSequenceID)
401 : {
402 : MessagePortServiceData* data;
403 0 : if (!mPorts.Get(aUUID, &data)) {
404 0 : NS_WARNING("Unknown MessagePort in ForceClose()");
405 0 : return true;
406 : }
407 :
408 0 : if (!data->mDestinationUUID.Equals(aDestinationUUID) ||
409 0 : data->mSequenceID != aSequenceID) {
410 0 : NS_WARNING("DestinationUUID and/or sequenceID do not match.");
411 0 : return false;
412 : }
413 :
414 0 : CloseAll(aUUID, true);
415 0 : return true;
416 : }
417 :
418 : } // namespace dom
419 : } // namespace mozilla
|