Coverage Report

Created: 2018-09-25 14:53

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