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