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 "ClientManager.h"
8
9 #include "ClientHandle.h"
10 #include "ClientManagerChild.h"
11 #include "ClientManagerOpChild.h"
12 #include "ClientSource.h"
13 #include "mozilla/dom/WorkerPrivate.h"
14 #include "mozilla/ipc/BackgroundChild.h"
15 #include "mozilla/ipc/PBackgroundChild.h"
16 #include "mozilla/ClearOnShutdown.h" // PastShutdownPhase
17 #include "mozilla/StaticPrefs_dom.h"
18 #include "prthread.h"
19
20 namespace mozilla::dom {
21
22 using mozilla::ipc::BackgroundChild;
23 using mozilla::ipc::PBackgroundChild;
24 using mozilla::ipc::PrincipalInfo;
25
26 namespace {
27
28 const uint32_t kBadThreadLocalIndex = -1;
29 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
30 const uint32_t kThreadLocalMagic1 = 0x8d57eea6;
31 const uint32_t kThreadLocalMagic2 = 0x59f375c9;
32 #endif
33
34 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
35 uint32_t sClientManagerThreadLocalMagic1 = kThreadLocalMagic1;
36 #endif
37
38 uint32_t sClientManagerThreadLocalIndex = kBadThreadLocalIndex;
39
40 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
41 uint32_t sClientManagerThreadLocalMagic2 = kThreadLocalMagic2;
42 uint32_t sClientManagerThreadLocalIndexDuplicate = kBadThreadLocalIndex;
43 #endif
44
45 } // anonymous namespace
46
ClientManager()47 ClientManager::ClientManager() {
48 PBackgroundChild* parentActor =
49 BackgroundChild::GetOrCreateForCurrentThread();
50 if (NS_WARN_IF(!parentActor)) {
51 Shutdown();
52 return;
53 }
54
55 ClientManagerChild* actor = ClientManagerChild::Create();
56 if (NS_WARN_IF(!actor)) {
57 Shutdown();
58 return;
59 }
60
61 PClientManagerChild* sentActor =
62 parentActor->SendPClientManagerConstructor(actor);
63 if (NS_WARN_IF(!sentActor)) {
64 Shutdown();
65 return;
66 }
67 MOZ_DIAGNOSTIC_ASSERT(sentActor == actor);
68
69 ActivateThing(actor);
70 }
71
~ClientManager()72 ClientManager::~ClientManager() {
73 NS_ASSERT_OWNINGTHREAD(ClientManager);
74
75 Shutdown();
76
77 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic1 == kThreadLocalMagic1);
78 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic2 == kThreadLocalMagic2);
79 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex != kBadThreadLocalIndex);
80 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex ==
81 sClientManagerThreadLocalIndexDuplicate);
82 MOZ_DIAGNOSTIC_ASSERT(this ==
83 PR_GetThreadPrivate(sClientManagerThreadLocalIndex));
84
85 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
86 PRStatus status =
87 #endif
88 PR_SetThreadPrivate(sClientManagerThreadLocalIndex, nullptr);
89 MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS);
90 }
91
Shutdown()92 void ClientManager::Shutdown() {
93 NS_ASSERT_OWNINGTHREAD(ClientManager);
94
95 if (IsShutdown()) {
96 return;
97 }
98
99 ShutdownThing();
100 }
101
CreateSourceInternal(ClientType aType,nsISerialEventTarget * aEventTarget,const PrincipalInfo & aPrincipal)102 UniquePtr<ClientSource> ClientManager::CreateSourceInternal(
103 ClientType aType, nsISerialEventTarget* aEventTarget,
104 const PrincipalInfo& aPrincipal) {
105 NS_ASSERT_OWNINGTHREAD(ClientManager);
106
107 nsID id;
108 nsresult rv = nsID::GenerateUUIDInPlace(id);
109 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
110 if (NS_WARN_IF(NS_FAILED(rv))) {
111 // If we can't even get a UUID, at least make sure not to use a garbage
112 // value. Instead return a shutdown ClientSource with a zero'd id.
113 // This should be exceptionally rare, if it happens at all.
114 id.Clear();
115 ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now());
116 UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
117 source->Shutdown();
118 return source;
119 }
120
121 ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now());
122 UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
123
124 if (IsShutdown()) {
125 source->Shutdown();
126 return source;
127 }
128
129 source->Activate(GetActor());
130
131 return source;
132 }
133
CreateSourceInternal(const ClientInfo & aClientInfo,nsISerialEventTarget * aEventTarget)134 UniquePtr<ClientSource> ClientManager::CreateSourceInternal(
135 const ClientInfo& aClientInfo, nsISerialEventTarget* aEventTarget) {
136 NS_ASSERT_OWNINGTHREAD(ClientManager);
137
138 ClientSourceConstructorArgs args(aClientInfo.Id(), aClientInfo.Type(),
139 aClientInfo.PrincipalInfo(),
140 aClientInfo.CreationTime());
141 UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
142
143 if (IsShutdown()) {
144 source->Shutdown();
145 return source;
146 }
147
148 source->Activate(GetActor());
149
150 return source;
151 }
152
CreateHandleInternal(const ClientInfo & aClientInfo,nsISerialEventTarget * aSerialEventTarget)153 already_AddRefed<ClientHandle> ClientManager::CreateHandleInternal(
154 const ClientInfo& aClientInfo, nsISerialEventTarget* aSerialEventTarget) {
155 NS_ASSERT_OWNINGTHREAD(ClientManager);
156 MOZ_DIAGNOSTIC_ASSERT(aSerialEventTarget);
157
158 RefPtr<ClientHandle> handle =
159 new ClientHandle(this, aSerialEventTarget, aClientInfo);
160
161 if (IsShutdown()) {
162 handle->Shutdown();
163 return handle.forget();
164 }
165
166 handle->Activate(GetActor());
167
168 return handle.forget();
169 }
170
StartOp(const ClientOpConstructorArgs & aArgs,nsISerialEventTarget * aSerialEventTarget)171 RefPtr<ClientOpPromise> ClientManager::StartOp(
172 const ClientOpConstructorArgs& aArgs,
173 nsISerialEventTarget* aSerialEventTarget) {
174 RefPtr<ClientOpPromise::Private> promise =
175 new ClientOpPromise::Private(__func__);
176
177 // Hold a ref to the client until the remote operation completes. Otherwise
178 // the ClientHandle might get de-refed and teardown the actor before we
179 // get an answer.
180 RefPtr<ClientManager> kungFuGrip = this;
181
182 MaybeExecute(
183 [&aArgs, promise, kungFuGrip](ClientManagerChild* aActor) {
184 ClientManagerOpChild* actor =
185 new ClientManagerOpChild(kungFuGrip, aArgs, promise);
186 if (!aActor->SendPClientManagerOpConstructor(actor, aArgs)) {
187 // Constructor failure will reject promise via ActorDestroy()
188 return;
189 }
190 },
191 [promise] {
192 CopyableErrorResult rv;
193 rv.ThrowInvalidStateError("Client has been destroyed");
194 promise->Reject(rv, __func__);
195 });
196
197 return promise;
198 }
199
200 // static
GetOrCreateForCurrentThread()201 already_AddRefed<ClientManager> ClientManager::GetOrCreateForCurrentThread() {
202 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic1 == kThreadLocalMagic1);
203 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic2 == kThreadLocalMagic2);
204 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex != kBadThreadLocalIndex);
205 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex ==
206 sClientManagerThreadLocalIndexDuplicate);
207 RefPtr<ClientManager> cm = static_cast<ClientManager*>(
208 PR_GetThreadPrivate(sClientManagerThreadLocalIndex));
209
210 if (!cm) {
211 cm = new ClientManager();
212
213 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
214 PRStatus status =
215 #endif
216 PR_SetThreadPrivate(sClientManagerThreadLocalIndex, cm.get());
217 MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS);
218 }
219
220 MOZ_DIAGNOSTIC_ASSERT(cm);
221
222 if (StaticPrefs::dom_workers_testing_enabled()) {
223 // Check that the ClientManager instance associated to the current thread
224 // has not been kept alive when it was expected to have been already
225 // deallocated (e.g. due to a leak ClientManager's mShutdown can have ben
226 // set to true from its RevokeActor method but never fully deallocated and
227 // unset from the thread locals).
228 MOZ_DIAGNOSTIC_ASSERT(!cm->IsShutdown());
229 }
230 return cm.forget();
231 }
232
GetWorkerPrivate() const233 WorkerPrivate* ClientManager::GetWorkerPrivate() const {
234 NS_ASSERT_OWNINGTHREAD(ClientManager);
235 MOZ_DIAGNOSTIC_ASSERT(GetActor());
236 return GetActor()->GetWorkerPrivate();
237 }
238
239 // Used to share logic between ExpectFutureSource and ForgetFutureSource.
ExpectOrForgetFutureSource(const ClientInfo & aClientInfo,bool (PClientManagerChild::* aMethod)(const IPCClientInfo &))240 /* static */ bool ClientManager::ExpectOrForgetFutureSource(
241 const ClientInfo& aClientInfo,
242 bool (PClientManagerChild::*aMethod)(const IPCClientInfo&)) {
243 // Return earlier if called late in the XPCOM shutdown path,
244 // ClientManager would be already shutdown at the point.
245 if (NS_WARN_IF(PastShutdownPhase(ShutdownPhase::XPCOMShutdown))) {
246 return false;
247 }
248
249 bool rv = true;
250
251 RefPtr<ClientManager> mgr = ClientManager::GetOrCreateForCurrentThread();
252 mgr->MaybeExecute(
253 [&](ClientManagerChild* aActor) {
254 if (!(aActor->*aMethod)(aClientInfo.ToIPC())) {
255 rv = false;
256 }
257 },
258 [&] { rv = false; });
259
260 return rv;
261 }
262
ExpectFutureSource(const ClientInfo & aClientInfo)263 /* static */ bool ClientManager::ExpectFutureSource(
264 const ClientInfo& aClientInfo) {
265 return ExpectOrForgetFutureSource(
266 aClientInfo, &PClientManagerChild::SendExpectFutureClientSource);
267 }
268
ForgetFutureSource(const ClientInfo & aClientInfo)269 /* static */ bool ClientManager::ForgetFutureSource(
270 const ClientInfo& aClientInfo) {
271 return ExpectOrForgetFutureSource(
272 aClientInfo, &PClientManagerChild::SendForgetFutureClientSource);
273 }
274
275 // static
Startup()276 void ClientManager::Startup() {
277 MOZ_ASSERT(NS_IsMainThread());
278
279 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic1 == kThreadLocalMagic1);
280 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic2 == kThreadLocalMagic2);
281 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex == kBadThreadLocalIndex);
282 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex ==
283 sClientManagerThreadLocalIndexDuplicate);
284
285 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
286 PRStatus status =
287 #endif
288 PR_NewThreadPrivateIndex(&sClientManagerThreadLocalIndex, nullptr);
289 MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS);
290
291 MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex != kBadThreadLocalIndex);
292 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
293 sClientManagerThreadLocalIndexDuplicate = sClientManagerThreadLocalIndex;
294 #endif
295 }
296
297 // static
CreateSource(ClientType aType,nsISerialEventTarget * aEventTarget,nsIPrincipal * aPrincipal)298 UniquePtr<ClientSource> ClientManager::CreateSource(
299 ClientType aType, nsISerialEventTarget* aEventTarget,
300 nsIPrincipal* aPrincipal) {
301 MOZ_ASSERT(NS_IsMainThread());
302 MOZ_ASSERT(aPrincipal);
303
304 PrincipalInfo principalInfo;
305 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
306 if (NS_WARN_IF(NS_FAILED(rv))) {
307 MOZ_CRASH("ClientManager::CreateSource() cannot serialize bad principal");
308 }
309
310 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
311 return mgr->CreateSourceInternal(aType, aEventTarget, principalInfo);
312 }
313
314 // static
CreateSource(ClientType aType,nsISerialEventTarget * aEventTarget,const PrincipalInfo & aPrincipal)315 UniquePtr<ClientSource> ClientManager::CreateSource(
316 ClientType aType, nsISerialEventTarget* aEventTarget,
317 const PrincipalInfo& aPrincipal) {
318 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
319 return mgr->CreateSourceInternal(aType, aEventTarget, aPrincipal);
320 }
321
322 // static
CreateSourceFromInfo(const ClientInfo & aClientInfo,nsISerialEventTarget * aEventTarget)323 UniquePtr<ClientSource> ClientManager::CreateSourceFromInfo(
324 const ClientInfo& aClientInfo, nsISerialEventTarget* aEventTarget) {
325 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
326 return mgr->CreateSourceInternal(aClientInfo, aEventTarget);
327 }
328
CreateInfo(ClientType aType,nsIPrincipal * aPrincipal)329 Maybe<ClientInfo> ClientManager::CreateInfo(ClientType aType,
330 nsIPrincipal* aPrincipal) {
331 MOZ_ASSERT(NS_IsMainThread());
332 MOZ_ASSERT(aPrincipal);
333
334 PrincipalInfo principalInfo;
335 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
336 if (NS_WARN_IF(NS_FAILED(rv))) {
337 MOZ_CRASH("ClientManager::CreateSource() cannot serialize bad principal");
338 }
339
340 nsID id;
341 rv = nsID::GenerateUUIDInPlace(id);
342 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
343 if (NS_WARN_IF(NS_FAILED(rv))) {
344 return Nothing();
345 }
346
347 return Some(ClientInfo(id, aType, principalInfo, TimeStamp::Now()));
348 }
349
350 // static
CreateHandle(const ClientInfo & aClientInfo,nsISerialEventTarget * aSerialEventTarget)351 already_AddRefed<ClientHandle> ClientManager::CreateHandle(
352 const ClientInfo& aClientInfo, nsISerialEventTarget* aSerialEventTarget) {
353 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
354 return mgr->CreateHandleInternal(aClientInfo, aSerialEventTarget);
355 }
356
357 // static
MatchAll(const ClientMatchAllArgs & aArgs,nsISerialEventTarget * aSerialEventTarget)358 RefPtr<ClientOpPromise> ClientManager::MatchAll(
359 const ClientMatchAllArgs& aArgs, nsISerialEventTarget* aSerialEventTarget) {
360 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
361 return mgr->StartOp(aArgs, aSerialEventTarget);
362 }
363
364 // static
Claim(const ClientClaimArgs & aArgs,nsISerialEventTarget * aSerialEventTarget)365 RefPtr<ClientOpPromise> ClientManager::Claim(
366 const ClientClaimArgs& aArgs, nsISerialEventTarget* aSerialEventTarget) {
367 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
368 return mgr->StartOp(aArgs, aSerialEventTarget);
369 }
370
371 // static
GetInfoAndState(const ClientGetInfoAndStateArgs & aArgs,nsISerialEventTarget * aSerialEventTarget)372 RefPtr<ClientOpPromise> ClientManager::GetInfoAndState(
373 const ClientGetInfoAndStateArgs& aArgs,
374 nsISerialEventTarget* aSerialEventTarget) {
375 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
376 return mgr->StartOp(aArgs, aSerialEventTarget);
377 }
378
379 // static
Navigate(const ClientNavigateArgs & aArgs,nsISerialEventTarget * aSerialEventTarget)380 RefPtr<ClientOpPromise> ClientManager::Navigate(
381 const ClientNavigateArgs& aArgs, nsISerialEventTarget* aSerialEventTarget) {
382 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
383 return mgr->StartOp(aArgs, aSerialEventTarget);
384 }
385
386 // static
OpenWindow(const ClientOpenWindowArgs & aArgs,nsISerialEventTarget * aSerialEventTarget)387 RefPtr<ClientOpPromise> ClientManager::OpenWindow(
388 const ClientOpenWindowArgs& aArgs,
389 nsISerialEventTarget* aSerialEventTarget) {
390 RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
391 return mgr->StartOp(aArgs, aSerialEventTarget);
392 }
393
394 } // namespace mozilla::dom
395