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