Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/clients/manager/ClientSourceParent.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 "ClientSourceParent.h"
8
9
#include "ClientHandleParent.h"
10
#include "ClientManagerService.h"
11
#include "ClientSourceOpParent.h"
12
#include "ClientValidation.h"
13
#include "mozilla/dom/ClientIPCTypes.h"
14
#include "mozilla/dom/ContentParent.h"
15
#include "mozilla/dom/PClientManagerParent.h"
16
#include "mozilla/ipc/BackgroundParent.h"
17
#include "mozilla/SystemGroup.h"
18
#include "mozilla/Unused.h"
19
20
namespace mozilla {
21
namespace dom {
22
23
using mozilla::ipc::AssertIsOnBackgroundThread;
24
using mozilla::ipc::BackgroundParent;
25
using mozilla::ipc::IPCResult;
26
using mozilla::ipc::PrincipalInfo;
27
28
namespace {
29
30
// It would be nice to use a lambda instead of this class, but we cannot
31
// move capture in lambdas yet and ContentParent cannot be AddRef'd off
32
// the main thread.
33
class KillContentParentRunnable final : public Runnable
34
{
35
  RefPtr<ContentParent> mContentParent;
36
37
public:
38
  explicit KillContentParentRunnable(RefPtr<ContentParent>&& aContentParent)
39
    : Runnable("KillContentParentRunnable")
40
    , mContentParent(std::move(aContentParent))
41
0
  {
42
0
    MOZ_ASSERT(mContentParent);
43
0
  }
44
45
  NS_IMETHOD
46
  Run() override
47
0
  {
48
0
    MOZ_ASSERT(NS_IsMainThread());
49
0
    mContentParent->KillHard("invalid ClientSourceParent actor");
50
0
    mContentParent = nullptr;
51
0
    return NS_OK;
52
0
  }
53
};
54
55
} // anonymous namespace
56
57
void
58
ClientSourceParent::KillInvalidChild()
59
0
{
60
0
  // Try to get the content process before we destroy the actor below.
61
0
  RefPtr<ContentParent> process =
62
0
    BackgroundParent::GetContentParent(Manager()->Manager());
63
0
64
0
  // First, immediately teardown the ClientSource actor.  No matter what
65
0
  // we want to start this process as soon as possible.
66
0
  Unused << ClientSourceParent::Send__delete__(this);
67
0
68
0
  // If we are running in non-e10s, then there is nothing else to do here.
69
0
  // There is no child process and we don't want to crash the entire browser
70
0
  // in release builds.  In general, though, this should not happen in non-e10s
71
0
  // so we do assert this condition.
72
0
  if (!process) {
73
0
    MOZ_DIAGNOSTIC_ASSERT(false, "invalid ClientSourceParent in non-e10s");
74
0
    return;
75
0
  }
76
0
77
0
  // In e10s mode we also want to kill the child process.  Validation failures
78
0
  // typically mean someone sent us bogus data over the IPC link.  We can't
79
0
  // trust that process any more.  We have to do this on the main thread, so
80
0
  // there is a small window of time before we kill the process.  This is why
81
0
  // we start the actor destruction immediately above.
82
0
  nsCOMPtr<nsIRunnable> r = new KillContentParentRunnable(std::move(process));
83
0
  MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
84
0
}
85
86
mozilla::ipc::IPCResult
87
ClientSourceParent::RecvWorkerSyncPing()
88
0
{
89
0
  AssertIsOnBackgroundThread();
90
0
  // Do nothing here.  This is purely a sync message allowing the child to
91
0
  // confirm that the actor has been created on the parent process.
92
0
  return IPC_OK();
93
0
}
94
95
IPCResult
96
ClientSourceParent::RecvTeardown()
97
0
{
98
0
  Unused << Send__delete__(this);
99
0
  return IPC_OK();
100
0
}
101
102
IPCResult
103
ClientSourceParent::RecvExecutionReady(const ClientSourceExecutionReadyArgs& aArgs)
104
0
{
105
0
  // Now that we have the creation URL for the Client we can do some validation
106
0
  // to make sure the child actor is not giving us garbage.  Since we validate
107
0
  // on the child side as well we treat a failure here as fatal.
108
0
  if (!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(), aArgs.url())) {
109
0
    KillInvalidChild();
110
0
    return IPC_OK();
111
0
  }
112
0
113
0
  mClientInfo.SetURL(aArgs.url());
114
0
  mClientInfo.SetFrameType(aArgs.frameType());
115
0
  mExecutionReady = true;
116
0
117
0
  for (ClientHandleParent* handle : mHandleList) {
118
0
    Unused << handle->SendExecutionReady(mClientInfo.ToIPC());
119
0
  }
120
0
121
0
  return IPC_OK();
122
0
};
123
124
IPCResult
125
ClientSourceParent::RecvFreeze()
126
0
{
127
0
  MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
128
0
  mFrozen = true;
129
0
130
0
  // Frozen clients should not be observable.  Act as if the client has
131
0
  // been destroyed.
132
0
  nsTArray<ClientHandleParent*> handleList(mHandleList);
133
0
  for (ClientHandleParent* handle : handleList) {
134
0
    Unused << ClientHandleParent::Send__delete__(handle);
135
0
  }
136
0
137
0
  return IPC_OK();
138
0
}
139
140
IPCResult
141
ClientSourceParent::RecvThaw()
142
0
{
143
0
  MOZ_DIAGNOSTIC_ASSERT(mFrozen);
144
0
  mFrozen = false;
145
0
  return IPC_OK();
146
0
}
147
148
IPCResult
149
ClientSourceParent::RecvInheritController(const ClientControlledArgs& aArgs)
150
0
{
151
0
  mController.reset();
152
0
  mController.emplace(aArgs.serviceWorker());
153
0
154
0
  // In parent-side intercept mode we must tell the parent-side SWM about
155
0
  // this controller inheritence.  In legacy client-side mode this is done
156
0
  // from the ClientSource instead.
157
0
  if (!ServiceWorkerParentInterceptEnabled()) {
158
0
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
159
0
      "ClientSourceParent::RecvInheritController",
160
0
      [clientInfo = mClientInfo, controller = mController.ref()] () {
161
0
        RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
162
0
        NS_ENSURE_TRUE_VOID(swm);
163
0
164
0
        swm->NoteInheritedController(clientInfo, controller);
165
0
      });
166
0
167
0
    MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
168
0
  }
169
0
170
0
  return IPC_OK();
171
0
}
172
173
IPCResult
174
ClientSourceParent::RecvNoteDOMContentLoaded()
175
0
{
176
0
  if (mController.isSome() && ServiceWorkerParentInterceptEnabled()) {
177
0
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
178
0
      "ClientSourceParent::RecvNoteDOMContentLoaded",
179
0
      [clientInfo = mClientInfo] () {
180
0
        RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
181
0
        NS_ENSURE_TRUE_VOID(swm);
182
0
183
0
        swm->MaybeCheckNavigationUpdate(clientInfo);
184
0
      });
185
0
186
0
    MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
187
0
  }
188
0
  return IPC_OK();
189
0
}
190
191
void
192
ClientSourceParent::ActorDestroy(ActorDestroyReason aReason)
193
0
{
194
0
  DebugOnly<bool> removed = mService->RemoveSource(this);
195
0
  MOZ_ASSERT(removed);
196
0
197
0
  nsTArray<ClientHandleParent*> handleList(mHandleList);
198
0
  for (ClientHandleParent* handle : handleList) {
199
0
    // This should trigger DetachHandle() to be called removing
200
0
    // the entry from the mHandleList.
201
0
    Unused << ClientHandleParent::Send__delete__(handle);
202
0
  }
203
0
  MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
204
0
}
205
206
PClientSourceOpParent*
207
ClientSourceParent::AllocPClientSourceOpParent(const ClientOpConstructorArgs& aArgs)
208
0
{
209
0
  MOZ_ASSERT_UNREACHABLE("ClientSourceOpParent should be explicitly constructed.");
210
0
  return nullptr;
211
0
}
212
213
bool
214
ClientSourceParent::DeallocPClientSourceOpParent(PClientSourceOpParent* aActor)
215
0
{
216
0
  delete aActor;
217
0
  return true;
218
0
}
219
220
ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs)
221
  : mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
222
  , mService(ClientManagerService::GetOrCreateInstance())
223
  , mExecutionReady(false)
224
  , mFrozen(false)
225
0
{
226
0
}
227
228
ClientSourceParent::~ClientSourceParent()
229
0
{
230
0
  MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
231
0
}
232
233
void
234
ClientSourceParent::Init()
235
0
{
236
0
  // Ensure the principal is reasonable before adding ourself to the service.
237
0
  // Since we validate the principal on the child side as well, any failure
238
0
  // here is treated as fatal.
239
0
  if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) {
240
0
    KillInvalidChild();
241
0
    return;
242
0
  }
243
0
244
0
  // Its possible for AddSource() to fail if there is already an entry for
245
0
  // our UUID.  This should not normally happen, but could if someone is
246
0
  // spoofing IPC messages.
247
0
  if (NS_WARN_IF(!mService->AddSource(this))) {
248
0
    KillInvalidChild();
249
0
    return;
250
0
  }
251
0
}
252
253
const ClientInfo&
254
ClientSourceParent::Info() const
255
0
{
256
0
  return mClientInfo;
257
0
}
258
259
bool
260
ClientSourceParent::IsFrozen() const
261
0
{
262
0
  return mFrozen;
263
0
}
264
265
bool
266
ClientSourceParent::ExecutionReady() const
267
0
{
268
0
  return mExecutionReady;
269
0
}
270
271
const Maybe<ServiceWorkerDescriptor>&
272
ClientSourceParent::GetController() const
273
0
{
274
0
  return mController;
275
0
}
276
277
void
278
ClientSourceParent::ClearController()
279
0
{
280
0
  mController.reset();
281
0
}
282
283
void
284
ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle)
285
0
{
286
0
  MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
287
0
  MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
288
0
  MOZ_ASSERT(!mHandleList.Contains(aClientHandle));
289
0
  mHandleList.AppendElement(aClientHandle);
290
0
}
291
292
void
293
ClientSourceParent::DetachHandle(ClientHandleParent* aClientHandle)
294
0
{
295
0
  MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
296
0
  MOZ_ASSERT(mHandleList.Contains(aClientHandle));
297
0
  mHandleList.RemoveElement(aClientHandle);
298
0
}
299
300
RefPtr<ClientOpPromise>
301
ClientSourceParent::StartOp(const ClientOpConstructorArgs& aArgs)
302
0
{
303
0
  RefPtr<ClientOpPromise::Private> promise =
304
0
    new ClientOpPromise::Private(__func__);
305
0
306
0
  // If we are being controlled, remember that data before propagating
307
0
  // on to the ClientSource.  This must be set prior to triggering
308
0
  // the controllerchange event from the ClientSource since some tests
309
0
  // expect matchAll() to find the controlled client immediately after.
310
0
  // If the control operation fails, then we reset the controller value
311
0
  // to reflect the final state.
312
0
  if (aArgs.type() == ClientOpConstructorArgs::TClientControlledArgs) {
313
0
    mController.reset();
314
0
    mController.emplace(aArgs.get_ClientControlledArgs().serviceWorker());
315
0
  }
316
0
317
0
  // Constructor failure will reject the promise via ActorDestroy().
318
0
  ClientSourceOpParent* actor = new ClientSourceOpParent(aArgs, promise);
319
0
  Unused << SendPClientSourceOpConstructor(actor, aArgs);
320
0
321
0
  return promise.forget();
322
0
}
323
324
} // namespace dom
325
} // namespace mozilla