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 "RemoteWorkerChild.h"
8 
9 #include <utility>
10 
11 #include "MainThreadUtils.h"
12 #include "nsCOMPtr.h"
13 #include "nsDebug.h"
14 #include "nsError.h"
15 #include "nsIConsoleReportCollector.h"
16 #include "nsIInterfaceRequestor.h"
17 #include "nsIPrincipal.h"
18 #include "nsNetUtil.h"
19 #include "nsProxyRelease.h"
20 #include "nsThreadUtils.h"
21 #include "nsXULAppAPI.h"
22 
23 #include "RemoteWorkerService.h"
24 #include "mozilla/ArrayAlgorithm.h"
25 #include "mozilla/Assertions.h"
26 #include "mozilla/Attributes.h"
27 #include "mozilla/BasePrincipal.h"
28 #include "mozilla/ErrorResult.h"
29 #include "mozilla/SchedulerGroup.h"
30 #include "mozilla/Services.h"
31 #include "mozilla/ScopeExit.h"
32 #include "mozilla/Unused.h"
33 #include "mozilla/dom/FetchEventOpProxyChild.h"
34 #include "mozilla/dom/IndexedDatabaseManager.h"
35 #include "mozilla/dom/MessagePort.h"
36 #include "mozilla/dom/RemoteWorkerManager.h"  // RemoteWorkerManager::IsRemoteTypeAllowed
37 #include "mozilla/dom/RemoteWorkerTypes.h"
38 #include "mozilla/dom/ServiceWorkerDescriptor.h"
39 #include "mozilla/dom/ServiceWorkerInterceptController.h"
40 #include "mozilla/dom/ServiceWorkerOp.h"
41 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
42 #include "mozilla/dom/ServiceWorkerShutdownState.h"
43 #include "mozilla/dom/ServiceWorkerUtils.h"
44 #include "mozilla/dom/workerinternals/ScriptLoader.h"
45 #include "mozilla/dom/WorkerError.h"
46 #include "mozilla/dom/WorkerPrivate.h"
47 #include "mozilla/dom/WorkerRef.h"
48 #include "mozilla/dom/WorkerRunnable.h"
49 #include "mozilla/ipc/BackgroundUtils.h"
50 #include "mozilla/ipc/URIUtils.h"
51 #include "mozilla/net/CookieJarSettings.h"
52 #include "mozilla/PermissionManager.h"
53 
54 namespace mozilla {
55 
56 using namespace ipc;
57 
58 namespace dom {
59 
60 using workerinternals::ChannelFromScriptURLMainThread;
61 
62 class SelfHolder {
63  public:
SelfHolder(RemoteWorkerChild * aSelf)64   MOZ_IMPLICIT SelfHolder(RemoteWorkerChild* aSelf) : mSelf(aSelf) {
65     MOZ_ASSERT(mSelf);
66   }
67 
68   SelfHolder(const SelfHolder&) = default;
69 
70   SelfHolder& operator=(const SelfHolder&) = default;
71 
72   SelfHolder(SelfHolder&&) = default;
73 
74   SelfHolder& operator=(SelfHolder&&) = default;
75 
~SelfHolder()76   ~SelfHolder() {
77     if (!mSelf) {
78       return;
79     }
80 
81     nsCOMPtr<nsISerialEventTarget> target = mSelf->GetOwningEventTarget();
82     NS_ProxyRelease("SelfHolder::mSelf", target, mSelf.forget());
83   }
84 
get() const85   RemoteWorkerChild* get() const {
86     MOZ_ASSERT(mSelf);
87 
88     return mSelf.get();
89   }
90 
operator ->() const91   RemoteWorkerChild* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
92     return get();
93   }
94 
operator !()95   bool operator!() { return !mSelf.get(); }
96 
97  private:
98   RefPtr<RemoteWorkerChild> mSelf;
99 };
100 
101 namespace {
102 
103 class SharedWorkerInterfaceRequestor final : public nsIInterfaceRequestor {
104  public:
105   NS_DECL_ISUPPORTS
106 
SharedWorkerInterfaceRequestor()107   SharedWorkerInterfaceRequestor() {
108     // This check must match the code nsDocShell::Create.
109     if (XRE_IsParentProcess()) {
110       mSWController = new ServiceWorkerInterceptController();
111     }
112   }
113 
114   NS_IMETHOD
GetInterface(const nsIID & aIID,void ** aSink)115   GetInterface(const nsIID& aIID, void** aSink) override {
116     MOZ_ASSERT(NS_IsMainThread());
117 
118     if (mSWController &&
119         aIID.Equals(NS_GET_IID(nsINetworkInterceptController))) {
120       // If asked for the network intercept controller, ask the outer requestor,
121       // which could be the docshell.
122       RefPtr<ServiceWorkerInterceptController> swController = mSWController;
123       swController.forget(aSink);
124       return NS_OK;
125     }
126 
127     return NS_NOINTERFACE;
128   }
129 
130  private:
131   ~SharedWorkerInterfaceRequestor() = default;
132 
133   RefPtr<ServiceWorkerInterceptController> mSWController;
134 };
135 
136 NS_IMPL_ADDREF(SharedWorkerInterfaceRequestor)
137 NS_IMPL_RELEASE(SharedWorkerInterfaceRequestor)
138 NS_IMPL_QUERY_INTERFACE(SharedWorkerInterfaceRequestor, nsIInterfaceRequestor)
139 
140 // Normal runnable because AddPortIdentifier() is going to exec JS code.
141 class MessagePortIdentifierRunnable final : public WorkerRunnable {
142  public:
MessagePortIdentifierRunnable(WorkerPrivate * aWorkerPrivate,SelfHolder aActor,const MessagePortIdentifier & aPortIdentifier)143   MessagePortIdentifierRunnable(WorkerPrivate* aWorkerPrivate,
144                                 SelfHolder aActor,
145                                 const MessagePortIdentifier& aPortIdentifier)
146       : WorkerRunnable(aWorkerPrivate),
147         mActor(std::move(aActor)),
148         mPortIdentifier(aPortIdentifier) {}
149 
150  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)151   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
152     mActor->AddPortIdentifier(aCx, aWorkerPrivate, mPortIdentifier);
153     return true;
154   }
155 
156   SelfHolder mActor;
157   UniqueMessagePortId mPortIdentifier;
158 };
159 
160 // This is used to release WeakWorkerRefs which can only have their refcount
161 // modified on the owning thread (worker thread in this case). It also keeps
162 // alive the associated WorkerPrivate until the WeakWorkerRef is released.
163 class ReleaseWorkerRunnable final : public WorkerControlRunnable {
164  public:
ReleaseWorkerRunnable(RefPtr<WorkerPrivate> && aWorkerPrivate,RefPtr<WeakWorkerRef> && aWeakRef)165   ReleaseWorkerRunnable(RefPtr<WorkerPrivate>&& aWorkerPrivate,
166                         RefPtr<WeakWorkerRef>&& aWeakRef)
167       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
168         mWorkerPrivate(std::move(aWorkerPrivate)),
169         mWeakRef(std::move(aWeakRef)) {
170     MOZ_ASSERT(mWorkerPrivate);
171     MOZ_ASSERT(!mWorkerPrivate->IsOnWorkerThread());
172     MOZ_ASSERT(mWeakRef);
173   }
174 
175  private:
~ReleaseWorkerRunnable()176   ~ReleaseWorkerRunnable() { ReleaseMembers(); }
177 
WorkerRun(JSContext *,WorkerPrivate *)178   bool WorkerRun(JSContext*, WorkerPrivate*) override {
179     ReleaseMembers();
180     return true;
181   }
182 
Cancel()183   nsresult Cancel() override {
184     ReleaseMembers();
185     return NS_OK;
186   }
187 
ReleaseMembers()188   void ReleaseMembers() {
189     if (!mWorkerPrivate) {
190       MOZ_ASSERT(!mWeakRef);
191       return;
192     }
193 
194     mWeakRef = nullptr;
195 
196     NS_ReleaseOnMainThread("ReleaseWorkerRunnable::mWorkerPrivate",
197                            mWorkerPrivate.forget());
198   }
199 
200   RefPtr<WorkerPrivate> mWorkerPrivate;
201   RefPtr<WeakWorkerRef> mWeakRef;
202 };
203 
204 }  // anonymous namespace
205 
206 class RemoteWorkerChild::InitializeWorkerRunnable final
207     : public WorkerRunnable {
208  public:
InitializeWorkerRunnable(WorkerPrivate * aWorkerPrivate,SelfHolder aActor)209   InitializeWorkerRunnable(WorkerPrivate* aWorkerPrivate, SelfHolder aActor)
210       : WorkerRunnable(aWorkerPrivate), mActor(std::move(aActor)) {
211     MOZ_ASSERT(mActor);
212   }
213 
214  private:
~InitializeWorkerRunnable()215   ~InitializeWorkerRunnable() { MaybeAbort(); }
216 
WorkerRun(JSContext *,WorkerPrivate *)217   bool WorkerRun(JSContext*, WorkerPrivate*) override {
218     MOZ_ASSERT(mActor);
219 
220     mActor->InitializeOnWorker();
221 
222     SelfHolder holder = std::move(mActor);
223     MOZ_ASSERT(!mActor);
224 
225     return true;
226   }
227 
Cancel()228   nsresult Cancel() override {
229     MaybeAbort();
230 
231     return WorkerRunnable::Cancel();
232   }
233 
234   // Slowly running out of synonyms for cancel, abort, terminate, etc...
MaybeAbort()235   void MaybeAbort() {
236     if (!mActor) {
237       return;
238     }
239 
240     mActor->TransitionStateToTerminated();
241     mActor->CreationFailedOnAnyThread();
242     mActor->ShutdownOnWorker();
243 
244     SelfHolder holder = std::move(mActor);
245     MOZ_ASSERT(!mActor);
246   }
247 
248   // Falsy indicates that WorkerRun or MaybeAbort has already been called.
249   SelfHolder mActor;
250 };
251 
RemoteWorkerChild(const RemoteWorkerData & aData)252 RemoteWorkerChild::RemoteWorkerChild(const RemoteWorkerData& aData)
253     : mState(VariantType<Pending>(), "RemoteWorkerChild::mState"),
254       mIsServiceWorker(aData.serviceWorkerData().type() ==
255                        OptionalServiceWorkerData::TServiceWorkerData),
256       mOwningEventTarget(GetCurrentSerialEventTarget()) {
257   MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
258   MOZ_ASSERT(mOwningEventTarget);
259 }
260 
~RemoteWorkerChild()261 RemoteWorkerChild::~RemoteWorkerChild() {
262 #ifdef DEBUG
263   auto lock = mState.Lock();
264   MOZ_ASSERT(lock->is<Terminated>());
265 #endif
266 }
267 
GetOwningEventTarget() const268 nsISerialEventTarget* RemoteWorkerChild::GetOwningEventTarget() const {
269   return mOwningEventTarget;
270 }
271 
ActorDestroy(ActorDestroyReason)272 void RemoteWorkerChild::ActorDestroy(ActorDestroyReason) {
273   auto launcherData = mLauncherData.Access();
274   launcherData->mIPCActive = false;
275 
276   Unused << NS_WARN_IF(!launcherData->mTerminationPromise.IsEmpty());
277   launcherData->mTerminationPromise.RejectIfExists(NS_ERROR_DOM_ABORT_ERR,
278                                                    __func__);
279 
280   auto lock = mState.Lock();
281 
282   Unused << NS_WARN_IF(!lock->is<Terminated>());
283 
284   *lock = VariantType<Terminated>();
285 }
286 
ExecWorker(const RemoteWorkerData & aData)287 void RemoteWorkerChild::ExecWorker(const RemoteWorkerData& aData) {
288 #ifdef DEBUG
289   MOZ_ASSERT(GetOwningEventTarget()->IsOnCurrentThread());
290   auto launcherData = mLauncherData.Access();
291   MOZ_ASSERT(launcherData->mIPCActive);
292 #endif
293 
294   SelfHolder self = this;
295 
296   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
297       __func__, [self = std::move(self), data = aData]() mutable {
298         nsresult rv = self->ExecWorkerOnMainThread(std::move(data));
299 
300         if (NS_WARN_IF(NS_FAILED(rv))) {
301           self->CreationFailedOnAnyThread();
302         }
303       });
304 
305   MOZ_ALWAYS_SUCCEEDS(
306       SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
307 }
308 
ExecWorkerOnMainThread(RemoteWorkerData && aData)309 nsresult RemoteWorkerChild::ExecWorkerOnMainThread(RemoteWorkerData&& aData) {
310   MOZ_ASSERT(NS_IsMainThread());
311 
312   // Ensure that the IndexedDatabaseManager is initialized
313   Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
314 
315   auto scopeExit = MakeScopeExit([&] { TransitionStateToTerminated(); });
316 
317   // Verify the the RemoteWorker should be really allowed to run in this
318   // process, and fail if it shouldn't (This shouldn't normally happen,
319   // unless the RemoteWorkerData has been tempered in the process it was
320   // sent from).
321   if (!RemoteWorkerManager::IsRemoteTypeAllowed(aData)) {
322     return NS_ERROR_UNEXPECTED;
323   }
324 
325   auto principalOrErr = PrincipalInfoToPrincipal(aData.principalInfo());
326   if (NS_WARN_IF(principalOrErr.isErr())) {
327     return principalOrErr.unwrapErr();
328   }
329 
330   nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
331 
332   auto loadingPrincipalOrErr =
333       PrincipalInfoToPrincipal(aData.loadingPrincipalInfo());
334   if (NS_WARN_IF(loadingPrincipalOrErr.isErr())) {
335     return loadingPrincipalOrErr.unwrapErr();
336   }
337 
338   auto partitionedPrincipalOrErr =
339       PrincipalInfoToPrincipal(aData.partitionedPrincipalInfo());
340   if (NS_WARN_IF(partitionedPrincipalOrErr.isErr())) {
341     return partitionedPrincipalOrErr.unwrapErr();
342   }
343 
344   WorkerLoadInfo info;
345   info.mBaseURI = DeserializeURI(aData.baseScriptURL());
346   info.mResolvedScriptURI = DeserializeURI(aData.resolvedScriptURL());
347 
348   info.mPrincipalInfo = MakeUnique<PrincipalInfo>(aData.principalInfo());
349   info.mPartitionedPrincipalInfo =
350       MakeUnique<PrincipalInfo>(aData.partitionedPrincipalInfo());
351 
352   info.mReferrerInfo = aData.referrerInfo();
353   info.mDomain = aData.domain();
354   info.mPrincipal = principal;
355   info.mPartitionedPrincipal = partitionedPrincipalOrErr.unwrap();
356   info.mLoadingPrincipal = loadingPrincipalOrErr.unwrap();
357   info.mStorageAccess = aData.storageAccess();
358   info.mUseRegularPrincipal = aData.useRegularPrincipal();
359   info.mHasStorageAccessPermissionGranted =
360       aData.hasStorageAccessPermissionGranted();
361   info.mOriginAttributes =
362       BasePrincipal::Cast(principal)->OriginAttributesRef();
363   net::CookieJarSettings::Deserialize(aData.cookieJarSettings(),
364                                       getter_AddRefs(info.mCookieJarSettings));
365 
366   // Default CSP permissions for now.  These will be overrided if necessary
367   // based on the script CSP headers during load in ScriptLoader.
368   info.mEvalAllowed = true;
369   info.mReportCSPViolations = false;
370   info.mSecureContext = aData.isSecureContext()
371                             ? WorkerLoadInfo::eSecureContext
372                             : WorkerLoadInfo::eInsecureContext;
373 
374   WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mLoadingPrincipal);
375 
376   RefPtr<SharedWorkerInterfaceRequestor> requestor =
377       new SharedWorkerInterfaceRequestor();
378   info.mInterfaceRequestor->SetOuterRequestor(requestor);
379 
380   Maybe<ClientInfo> clientInfo;
381   if (aData.clientInfo().isSome()) {
382     clientInfo.emplace(ClientInfo(aData.clientInfo().ref()));
383   }
384 
385   nsresult rv = NS_OK;
386 
387   if (clientInfo.isSome()) {
388     Maybe<mozilla::ipc::CSPInfo> cspInfo = clientInfo.ref().GetCspInfo();
389     if (cspInfo.isSome()) {
390       info.mCSP = CSPInfoToCSP(cspInfo.ref(), nullptr);
391       info.mCSPInfo = MakeUnique<CSPInfo>();
392       rv = CSPToCSPInfo(info.mCSP, info.mCSPInfo.get());
393       if (NS_WARN_IF(NS_FAILED(rv))) {
394         return rv;
395       }
396     }
397   }
398 
399   rv = info.SetPrincipalsAndCSPOnMainThread(
400       info.mPrincipal, info.mPartitionedPrincipal, info.mLoadGroup, info.mCSP);
401   if (NS_WARN_IF(NS_FAILED(rv))) {
402     return rv;
403   }
404 
405   nsString workerPrivateId;
406 
407   if (mIsServiceWorker) {
408     ServiceWorkerData& data = aData.serviceWorkerData().get_ServiceWorkerData();
409 
410     MOZ_ASSERT(!data.id().IsEmpty());
411     workerPrivateId = std::move(data.id());
412 
413     info.mServiceWorkerCacheName = data.cacheName();
414     info.mServiceWorkerDescriptor.emplace(data.descriptor());
415     info.mServiceWorkerRegistrationDescriptor.emplace(
416         data.registrationDescriptor());
417     info.mLoadFlags = static_cast<nsLoadFlags>(data.loadFlags());
418   } else {
419     // Top level workers' main script use the document charset for the script
420     // uri encoding.
421     rv = ChannelFromScriptURLMainThread(
422         info.mLoadingPrincipal, nullptr /* parent document */, info.mLoadGroup,
423         info.mResolvedScriptURI, clientInfo,
424         nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER, info.mCookieJarSettings,
425         info.mReferrerInfo, getter_AddRefs(info.mChannel));
426     if (NS_WARN_IF(NS_FAILED(rv))) {
427       return rv;
428     }
429   }
430 
431   info.mAgentClusterId = aData.agentClusterId();
432 
433   AutoJSAPI jsapi;
434   jsapi.Init();
435 
436   ErrorResult error;
437   RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
438       jsapi.cx(), aData.originalScriptURL(), false,
439       mIsServiceWorker ? WorkerKindService : WorkerKindShared, aData.name(),
440       VoidCString(), &info, error, std::move(workerPrivateId));
441 
442   if (NS_WARN_IF(error.Failed())) {
443     MOZ_ASSERT(!workerPrivate);
444 
445     rv = error.StealNSResult();
446     return rv;
447   }
448 
449   if (mIsServiceWorker) {
450     RefPtr<RemoteWorkerChild> self = this;
451     workerPrivate->SetRemoteWorkerControllerWeakRef(
452         ThreadSafeWeakPtr<RemoteWorkerChild>(self));
453   } else {
454     workerPrivate->SetRemoteWorkerController(this);
455   }
456 
457   RefPtr<InitializeWorkerRunnable> runnable =
458       new InitializeWorkerRunnable(workerPrivate, SelfHolder(this));
459 
460   {
461     MOZ_ASSERT(workerPrivate);
462     auto lock = mState.Lock();
463     lock->as<Pending>().mWorkerPrivate = std::move(workerPrivate);
464   }
465 
466   if (mIsServiceWorker) {
467     SelfHolder self = this;
468 
469     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
470         __func__, [initializeWorkerRunnable = std::move(runnable),
471                    self = std::move(self)] {
472           // Checking RemoteWorkerChild.mState
473           bool isPending;
474           {
475             auto lock = self->mState.Lock();
476             isPending = lock->is<Pending>();
477           }
478           if (NS_WARN_IF(!isPending || !initializeWorkerRunnable->Dispatch())) {
479             self->TransitionStateToTerminated();
480             self->CreationFailedOnAnyThread();
481           }
482         });
483 
484     RefPtr<PermissionManager> permissionManager =
485         PermissionManager::GetInstance();
486     if (!permissionManager) {
487       return NS_ERROR_FAILURE;
488     }
489     permissionManager->WhenPermissionsAvailable(principal, r);
490   } else {
491     if (NS_WARN_IF(!runnable->Dispatch())) {
492       rv = NS_ERROR_FAILURE;
493       return rv;
494     }
495   }
496 
497   scopeExit.release();
498 
499   return NS_OK;
500 }
501 
InitializeOnWorker()502 void RemoteWorkerChild::InitializeOnWorker() {
503   RefPtr<WorkerPrivate> workerPrivate;
504 
505   {
506     auto lock = mState.Lock();
507 
508     if (lock->is<PendingTerminated>()) {
509       TransitionStateToTerminated(lock.ref());
510       ShutdownOnWorker();
511       return;
512     }
513 
514     workerPrivate = std::move(lock->as<Pending>().mWorkerPrivate);
515   }
516 
517   MOZ_ASSERT(workerPrivate);
518   workerPrivate->AssertIsOnWorkerThread();
519 
520   RefPtr<RemoteWorkerChild> self = this;
521   ThreadSafeWeakPtr<RemoteWorkerChild> selfWeakRef(self);
522 
523   auto scopeExit = MakeScopeExit([&] {
524     MOZ_ASSERT(self);
525 
526     NS_ProxyRelease(__func__, mOwningEventTarget, self.forget());
527   });
528 
529   // Let RemoteWorkerChild own the WorkerPrivate; RemoteWorkerChild's state
530   // transitions should guarantee the WorkerPrivate is cleaned up correctly.
531   // This also reduces some complexity around thread lifetimes guarantees that
532   // RemoteWorkerChild's state transitions rely on (e.g. the worker thread
533   // terminating unexpectedly).
534   RefPtr<StrongWorkerRef> strongRef =
535       StrongWorkerRef::Create(workerPrivate, __func__);
536 
537   RefPtr<WeakWorkerRef> workerRef = WeakWorkerRef::Create(
538       workerPrivate, [selfWeakRef = std::move(selfWeakRef),
539                       strongRef = std::move(strongRef)]() mutable {
540         RefPtr<RemoteWorkerChild> self(selfWeakRef);
541 
542         if (NS_WARN_IF(!self)) {
543           return;
544         }
545 
546         self->TransitionStateToTerminated();
547         self->ShutdownOnWorker();
548 
549         nsCOMPtr<nsISerialEventTarget> target = self->GetOwningEventTarget();
550         NS_ProxyRelease(__func__, target, self.forget());
551       });
552 
553   if (NS_WARN_IF(!workerRef)) {
554     TransitionStateToTerminated();
555     CreationFailedOnAnyThread();
556     ShutdownOnWorker();
557 
558     return;
559   }
560 
561   TransitionStateToRunning(workerPrivate.forget(), workerRef.forget());
562   CreationSucceededOnAnyThread();
563 }
564 
ShutdownOnWorker()565 void RemoteWorkerChild::ShutdownOnWorker() {
566   RefPtr<RemoteWorkerChild> self = this;
567 
568   nsCOMPtr<nsIRunnable> r =
569       NS_NewRunnableFunction(__func__, [self = std::move(self)] {
570         auto launcherData = self->mLauncherData.Access();
571 
572         if (!launcherData->mIPCActive) {
573           return;
574         }
575 
576         launcherData->mIPCActive = false;
577         Unused << self->SendClose();
578       });
579 
580   GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
581 }
582 
GetTerminationPromise()583 RefPtr<GenericNonExclusivePromise> RemoteWorkerChild::GetTerminationPromise() {
584   auto launcherData = mLauncherData.Access();
585   return launcherData->mTerminationPromise.Ensure(__func__);
586 }
587 
CreationSucceededOnAnyThread()588 void RemoteWorkerChild::CreationSucceededOnAnyThread() {
589   CreationSucceededOrFailedOnAnyThread(true);
590 }
591 
CreationFailedOnAnyThread()592 void RemoteWorkerChild::CreationFailedOnAnyThread() {
593   CreationSucceededOrFailedOnAnyThread(false);
594 }
595 
CreationSucceededOrFailedOnAnyThread(bool aDidCreationSucceed)596 void RemoteWorkerChild::CreationSucceededOrFailedOnAnyThread(
597     bool aDidCreationSucceed) {
598 #ifdef DEBUG
599   auto lock = mState.Lock();
600   MOZ_ASSERT_IF(aDidCreationSucceed, lock->is<Running>());
601   MOZ_ASSERT_IF(!aDidCreationSucceed, lock->is<Terminated>());
602 #endif
603 
604   RefPtr<RemoteWorkerChild> self = this;
605 
606   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
607       __func__,
608       [self = std::move(self), didCreationSucceed = aDidCreationSucceed] {
609         auto launcherData = self->mLauncherData.Access();
610 
611         if (!launcherData->mIPCActive) {
612           return;
613         }
614 
615         Unused << self->SendCreated(didCreationSucceed);
616       });
617 
618   GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
619 }
620 
CloseWorkerOnMainThread(State & aState)621 void RemoteWorkerChild::CloseWorkerOnMainThread(State& aState) {
622   AssertIsOnMainThread();
623   MOZ_ASSERT(!aState.is<PendingTerminated>());
624 
625   // WeakWorkerRef callback will be asynchronously invoked after
626   // WorkerPrivate::Cancel.
627 
628   if (aState.is<Pending>()) {
629     aState.as<Pending>().mWorkerPrivate->Cancel();
630     TransitionStateToPendingTerminated(aState);
631     return;
632   }
633 
634   if (aState.is<Running>()) {
635     aState.as<Running>().mWorkerPrivate->Cancel();
636   }
637 }
638 
639 /**
640  * Error reporting method
641  */
ErrorPropagation(const ErrorValue & aValue)642 void RemoteWorkerChild::ErrorPropagation(const ErrorValue& aValue) {
643   MOZ_ASSERT(GetOwningEventTarget()->IsOnCurrentThread());
644 
645   auto launcherData = mLauncherData.Access();
646 
647   if (!launcherData->mIPCActive) {
648     return;
649   }
650 
651   Unused << SendError(aValue);
652 }
653 
ErrorPropagationDispatch(nsresult aError)654 void RemoteWorkerChild::ErrorPropagationDispatch(nsresult aError) {
655   MOZ_ASSERT(NS_FAILED(aError));
656 
657   RefPtr<RemoteWorkerChild> self = this;
658   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
659       "RemoteWorkerChild::ErrorPropagationDispatch",
660       [self = std::move(self), aError]() { self->ErrorPropagation(aError); });
661 
662   GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
663 }
664 
ErrorPropagationOnMainThread(const WorkerErrorReport * aReport,bool aIsErrorEvent)665 void RemoteWorkerChild::ErrorPropagationOnMainThread(
666     const WorkerErrorReport* aReport, bool aIsErrorEvent) {
667   AssertIsOnMainThread();
668 
669   ErrorValue value;
670   if (aIsErrorEvent) {
671     ErrorData data(
672         aReport->mIsWarning, aReport->mLineNumber, aReport->mColumnNumber,
673         aReport->mMessage, aReport->mFilename, aReport->mLine,
674         TransformIntoNewArray(aReport->mNotes, [](const WorkerErrorNote& note) {
675           return ErrorDataNote(note.mLineNumber, note.mColumnNumber,
676                                note.mMessage, note.mFilename);
677         }));
678     value = data;
679   } else {
680     value = void_t();
681   }
682 
683   RefPtr<RemoteWorkerChild> self = this;
684   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
685       "RemoteWorkerChild::ErrorPropagationOnMainThread",
686       [self = std::move(self), value]() { self->ErrorPropagation(value); });
687 
688   GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
689 }
690 
FlushReportsOnMainThread(nsIConsoleReportCollector * aReporter)691 void RemoteWorkerChild::FlushReportsOnMainThread(
692     nsIConsoleReportCollector* aReporter) {
693   AssertIsOnMainThread();
694 
695   bool reportErrorToBrowserConsole = true;
696 
697   // Flush the reports.
698   for (uint32_t i = 0, len = mWindowIDs.Length(); i < len; ++i) {
699     aReporter->FlushReportsToConsole(
700         mWindowIDs[i], nsIConsoleReportCollector::ReportAction::Save);
701     reportErrorToBrowserConsole = false;
702   }
703 
704   // Finally report to browser console if there is no any window.
705   if (reportErrorToBrowserConsole) {
706     aReporter->FlushReportsToConsole(0);
707     return;
708   }
709 
710   aReporter->ClearConsoleReports();
711 }
712 
713 /**
714  * Worker state transition methods
715  */
716 RemoteWorkerChild::WorkerPrivateAccessibleState::
~WorkerPrivateAccessibleState()717     ~WorkerPrivateAccessibleState() {
718   // mWorkerPrivate can be safely released on the main thread.
719   if (!mWorkerPrivate || NS_IsMainThread()) {
720     return;
721   }
722 
723   NS_ReleaseOnMainThread(
724       "RemoteWorkerChild::WorkerPrivateAccessibleState::mWorkerPrivate",
725       mWorkerPrivate.forget());
726 }
727 
~Running()728 RemoteWorkerChild::Running::~Running() {
729   // This can occur if the current object is a temporary.
730   if (!mWorkerPrivate) {
731     return;
732   }
733 
734   if (mWorkerPrivate->IsOnWorkerThread()) {
735     return;
736   }
737 
738   RefPtr<ReleaseWorkerRunnable> runnable = new ReleaseWorkerRunnable(
739       std::move(mWorkerPrivate), std::move(mWorkerRef));
740 
741   nsCOMPtr<nsIRunnable> dispatchWorkerRunnableRunnable =
742       NS_NewRunnableFunction(__func__, [runnable = std::move(runnable)] {
743         Unused << NS_WARN_IF(!runnable->Dispatch());
744       });
745 
746   if (NS_IsMainThread()) {
747     dispatchWorkerRunnableRunnable->Run();
748   } else {
749     SchedulerGroup::Dispatch(TaskCategory::Other,
750                              dispatchWorkerRunnableRunnable.forget());
751   }
752 }
753 
TransitionStateToPendingTerminated(State & aState)754 void RemoteWorkerChild::TransitionStateToPendingTerminated(State& aState) {
755   MOZ_ASSERT(aState.is<Pending>());
756 
757   CancelAllPendingOps(aState);
758 
759   aState = VariantType<PendingTerminated>();
760 }
761 
TransitionStateToRunning(already_AddRefed<WorkerPrivate> aWorkerPrivate,already_AddRefed<WeakWorkerRef> aWorkerRef)762 void RemoteWorkerChild::TransitionStateToRunning(
763     already_AddRefed<WorkerPrivate> aWorkerPrivate,
764     already_AddRefed<WeakWorkerRef> aWorkerRef) {
765   RefPtr<WorkerPrivate> workerPrivate = aWorkerPrivate;
766   MOZ_ASSERT(workerPrivate);
767 
768   RefPtr<WeakWorkerRef> workerRef = aWorkerRef;
769   MOZ_ASSERT(workerRef);
770 
771   auto lock = mState.Lock();
772 
773   MOZ_ASSERT(lock->is<Pending>());
774 
775   auto pendingOps = std::move(lock->as<Pending>().mPendingOps);
776 
777   /**
778    * I'd initialize the WorkerPrivate and WeakWorkerRef in the constructor,
779    * but mozilla::Variant attempts to call the thread-unsafe `AddRef()` on
780    * WorkerPrivate.
781    */
782   *lock = VariantType<Running>();
783   lock->as<Running>().mWorkerPrivate = std::move(workerPrivate);
784   lock->as<Running>().mWorkerRef = std::move(workerRef);
785 
786   SelfHolder self = this;
787 
788   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
789       __func__, [pendingOps = std::move(pendingOps), self = std::move(self)]() {
790         for (auto& op : pendingOps) {
791           auto lock = self->mState.Lock();
792 
793           DebugOnly<bool> started = op->MaybeStart(self.get(), lock.ref());
794           MOZ_ASSERT(started);
795         }
796       });
797 
798   MOZ_ALWAYS_SUCCEEDS(
799       mOwningEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
800 }
801 
TransitionStateToTerminated()802 void RemoteWorkerChild::TransitionStateToTerminated() {
803   auto lock = mState.Lock();
804 
805   TransitionStateToTerminated(lock.ref());
806 }
807 
TransitionStateToTerminated(State & aState)808 void RemoteWorkerChild::TransitionStateToTerminated(State& aState) {
809   if (aState.is<Pending>()) {
810     CancelAllPendingOps(aState);
811   }
812 
813   nsCOMPtr<nsIRunnable> r =
814       NS_NewRunnableFunction(__func__, [self = SelfHolder(this)]() {
815         auto launcherData = self->mLauncherData.Access();
816         launcherData->mTerminationPromise.ResolveIfExists(true, __func__);
817       });
818 
819   if (GetOwningEventTarget()->IsOnCurrentThread()) {
820     r->Run();
821   } else {
822     GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
823   }
824 
825   aState = VariantType<Terminated>();
826 }
827 
828 /**
829  * Operation execution classes/methods
830  */
831 class RemoteWorkerChild::SharedWorkerOp : public RemoteWorkerChild::Op {
832  public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerOp,override)833   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerOp, override)
834 
835   explicit SharedWorkerOp(RemoteWorkerOp&& aOp) : mOp(std::move(aOp)) {}
836 
MaybeStart(RemoteWorkerChild * aOwner,RemoteWorkerChild::State & aState)837   bool MaybeStart(RemoteWorkerChild* aOwner,
838                   RemoteWorkerChild::State& aState) override {
839     MOZ_ASSERT(!mStarted);
840     MOZ_ASSERT(aOwner);
841 
842     auto launcherData = aOwner->mLauncherData.Access();
843 
844     if (NS_WARN_IF(!launcherData->mIPCActive)) {
845       Unused << NS_WARN_IF(!aState.is<Terminated>());
846 
847 #ifdef DEBUG
848       mStarted = true;
849 #endif
850 
851       return true;
852     }
853 
854     if (aState.is<Pending>() && !IsTerminationOp()) {
855       return false;
856     }
857 
858     if (aState.is<PendingTerminated>() || aState.is<Terminated>()) {
859 #ifdef DEBUG
860       mStarted = true;
861 #endif
862 
863       return true;
864     }
865 
866     MOZ_ASSERT(aState.is<Running>() || IsTerminationOp());
867 
868     RefPtr<SharedWorkerOp> self = this;
869     SelfHolder owner = aOwner;
870 
871     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
872         __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
873           {
874             auto lock = owner->mState.Lock();
875 
876             if (NS_WARN_IF(lock->is<Terminated>())) {
877               self->Cancel();
878               return;
879             }
880           }
881 
882           self->Exec(owner);
883         });
884 
885     MOZ_ALWAYS_SUCCEEDS(
886         SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
887 
888 #ifdef DEBUG
889     mStarted = true;
890 #endif
891 
892     return true;
893   }
894 
Cancel()895   void Cancel() override {
896 #ifdef DEBUG
897     mStarted = true;
898 #endif
899   }
900 
901  private:
~SharedWorkerOp()902   ~SharedWorkerOp() { MOZ_ASSERT(mStarted); }
903 
IsTerminationOp() const904   bool IsTerminationOp() const {
905     return mOp.type() == RemoteWorkerOp::TRemoteWorkerTerminateOp;
906   }
907 
Exec(SelfHolder & aOwner)908   void Exec(SelfHolder& aOwner) {
909     using Running = RemoteWorkerChild::Running;
910 
911     AssertIsOnMainThread();
912 
913     auto lock = aOwner->mState.Lock();
914 
915     MOZ_ASSERT(lock->is<Running>() || IsTerminationOp());
916 
917     if (IsTerminationOp()) {
918       aOwner->CloseWorkerOnMainThread(lock.ref());
919       return;
920     }
921 
922     RefPtr<WorkerPrivate> workerPrivate = lock->as<Running>().mWorkerPrivate;
923 
924     MOZ_ASSERT(workerPrivate);
925 
926     if (mOp.type() == RemoteWorkerOp::TRemoteWorkerSuspendOp) {
927       workerPrivate->ParentWindowPaused();
928     } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerResumeOp) {
929       workerPrivate->ParentWindowResumed();
930     } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerFreezeOp) {
931       workerPrivate->Freeze(nullptr);
932     } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerThawOp) {
933       workerPrivate->Thaw(nullptr);
934     } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
935       RefPtr<MessagePortIdentifierRunnable> r =
936           new MessagePortIdentifierRunnable(
937               workerPrivate, aOwner,
938               mOp.get_RemoteWorkerPortIdentifierOp().portIdentifier());
939 
940       if (NS_WARN_IF(!r->Dispatch())) {
941         aOwner->ErrorPropagationDispatch(NS_ERROR_FAILURE);
942       }
943     } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerAddWindowIDOp) {
944       aOwner->mWindowIDs.AppendElement(
945           mOp.get_RemoteWorkerAddWindowIDOp().windowID());
946     } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerRemoveWindowIDOp) {
947       aOwner->mWindowIDs.RemoveElement(
948           mOp.get_RemoteWorkerRemoveWindowIDOp().windowID());
949     } else {
950       MOZ_CRASH("Unknown RemoteWorkerOp type!");
951     }
952   }
953 
954   RemoteWorkerOp mOp;
955 
956 #ifdef DEBUG
957   bool mStarted = false;
958 #endif
959 };
960 
AddPortIdentifier(JSContext * aCx,WorkerPrivate * aWorkerPrivate,UniqueMessagePortId & aPortIdentifier)961 void RemoteWorkerChild::AddPortIdentifier(
962     JSContext* aCx, WorkerPrivate* aWorkerPrivate,
963     UniqueMessagePortId& aPortIdentifier) {
964   if (NS_WARN_IF(!aWorkerPrivate->ConnectMessagePort(aCx, aPortIdentifier))) {
965     ErrorPropagationDispatch(NS_ERROR_FAILURE);
966   }
967 }
968 
CancelAllPendingOps(State & aState)969 void RemoteWorkerChild::CancelAllPendingOps(State& aState) {
970   MOZ_ASSERT(aState.is<Pending>());
971 
972   auto pendingOps = std::move(aState.as<Pending>().mPendingOps);
973 
974   for (auto& op : pendingOps) {
975     op->Cancel();
976   }
977 }
978 
MaybeStartOp(RefPtr<Op> && aOp)979 void RemoteWorkerChild::MaybeStartOp(RefPtr<Op>&& aOp) {
980   MOZ_ASSERT(aOp);
981 
982   auto lock = mState.Lock();
983 
984   if (!aOp->MaybeStart(this, lock.ref())) {
985     lock->as<Pending>().mPendingOps.AppendElement(std::move(aOp));
986   }
987 }
988 
RecvExecOp(RemoteWorkerOp && aOp)989 IPCResult RemoteWorkerChild::RecvExecOp(RemoteWorkerOp&& aOp) {
990   MOZ_ASSERT(!mIsServiceWorker);
991 
992   MaybeStartOp(new SharedWorkerOp(std::move(aOp)));
993 
994   return IPC_OK();
995 }
996 
RecvExecServiceWorkerOp(ServiceWorkerOpArgs && aArgs,ExecServiceWorkerOpResolver && aResolve)997 IPCResult RemoteWorkerChild::RecvExecServiceWorkerOp(
998     ServiceWorkerOpArgs&& aArgs, ExecServiceWorkerOpResolver&& aResolve) {
999   MOZ_ASSERT(mIsServiceWorker);
1000   MOZ_ASSERT(
1001       aArgs.type() != ServiceWorkerOpArgs::TServiceWorkerFetchEventOpArgs,
1002       "FetchEvent operations should be sent via PFetchEventOp(Proxy) actors!");
1003 
1004   MaybeReportServiceWorkerShutdownProgress(aArgs);
1005 
1006   MaybeStartOp(ServiceWorkerOp::Create(std::move(aArgs), std::move(aResolve)));
1007 
1008   return IPC_OK();
1009 }
1010 
1011 RefPtr<GenericPromise>
MaybeSendSetServiceWorkerSkipWaitingFlag()1012 RemoteWorkerChild::MaybeSendSetServiceWorkerSkipWaitingFlag() {
1013   RefPtr<GenericPromise::Private> promise =
1014       new GenericPromise::Private(__func__);
1015 
1016   RefPtr<RemoteWorkerChild> self = this;
1017 
1018   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self = std::move(
1019                                                                   self),
1020                                                               promise] {
1021     auto launcherData = self->mLauncherData.Access();
1022 
1023     if (!launcherData->mIPCActive) {
1024       promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
1025       return;
1026     }
1027 
1028     self->SendSetServiceWorkerSkipWaitingFlag()->Then(
1029         GetCurrentSerialEventTarget(), __func__,
1030         [promise](
1031             const SetServiceWorkerSkipWaitingFlagPromise::ResolveOrRejectValue&
1032                 aResult) {
1033           if (NS_WARN_IF(aResult.IsReject())) {
1034             promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
1035             return;
1036           }
1037 
1038           promise->Resolve(aResult.ResolveValue(), __func__);
1039         });
1040   });
1041 
1042   GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
1043 
1044   return promise;
1045 }
1046 
1047 /**
1048  * PFetchEventOpProxy methods
1049  */
AllocPFetchEventOpProxyChild(const ServiceWorkerFetchEventOpArgs & aArgs)1050 PFetchEventOpProxyChild* RemoteWorkerChild::AllocPFetchEventOpProxyChild(
1051     const ServiceWorkerFetchEventOpArgs& aArgs) {
1052   RefPtr<FetchEventOpProxyChild> actor = new FetchEventOpProxyChild();
1053 
1054   return actor.forget().take();
1055 }
1056 
RecvPFetchEventOpProxyConstructor(PFetchEventOpProxyChild * aActor,const ServiceWorkerFetchEventOpArgs & aArgs)1057 IPCResult RemoteWorkerChild::RecvPFetchEventOpProxyConstructor(
1058     PFetchEventOpProxyChild* aActor,
1059     const ServiceWorkerFetchEventOpArgs& aArgs) {
1060   MOZ_ASSERT(aActor);
1061 
1062   (static_cast<FetchEventOpProxyChild*>(aActor))->Initialize(aArgs);
1063 
1064   return IPC_OK();
1065 }
1066 
DeallocPFetchEventOpProxyChild(PFetchEventOpProxyChild * aActor)1067 bool RemoteWorkerChild::DeallocPFetchEventOpProxyChild(
1068     PFetchEventOpProxyChild* aActor) {
1069   MOZ_ASSERT(aActor);
1070 
1071   RefPtr<FetchEventOpProxyChild> actor =
1072       dont_AddRef(static_cast<FetchEventOpProxyChild*>(aActor));
1073 
1074   return true;
1075 }
1076 
1077 }  // namespace dom
1078 }  // namespace mozilla
1079