Source code

Revision control

Other Tools

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