Backed out 6 changesets (bug 1835907) for causing multiple failures. CLOSED TREE
[gecko.git] / dom / workers / remoteworkers / RemoteWorkerChild.cpp
blob9ca04021770a644dc0dfa7ac4f3ad2bc50b1ec4c
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/. */
7 #include "RemoteWorkerChild.h"
9 #include <utility>
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 "nsThreadUtils.h"
20 #include "nsXULAppAPI.h"
22 #include "RemoteWorkerService.h"
23 #include "mozilla/ArrayAlgorithm.h"
24 #include "mozilla/Assertions.h"
25 #include "mozilla/Attributes.h"
26 #include "mozilla/BasePrincipal.h"
27 #include "mozilla/ErrorResult.h"
28 #include "mozilla/SchedulerGroup.h"
29 #include "mozilla/Services.h"
30 #include "mozilla/ScopeExit.h"
31 #include "mozilla/Unused.h"
32 #include "mozilla/dom/FetchEventOpProxyChild.h"
33 #include "mozilla/dom/IndexedDatabaseManager.h"
34 #include "mozilla/dom/MessagePort.h"
35 #include "mozilla/dom/RemoteWorkerManager.h" // RemoteWorkerManager::IsRemoteTypeAllowed
36 #include "mozilla/dom/RemoteWorkerTypes.h"
37 #include "mozilla/dom/ServiceWorkerDescriptor.h"
38 #include "mozilla/dom/ServiceWorkerInterceptController.h"
39 #include "mozilla/dom/ServiceWorkerOp.h"
40 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
41 #include "mozilla/dom/ServiceWorkerShutdownState.h"
42 #include "mozilla/dom/ServiceWorkerUtils.h"
43 #include "mozilla/dom/workerinternals/ScriptLoader.h"
44 #include "mozilla/dom/WorkerError.h"
45 #include "mozilla/dom/WorkerPrivate.h"
46 #include "mozilla/dom/WorkerRef.h"
47 #include "mozilla/dom/WorkerRunnable.h"
48 #include "mozilla/dom/WorkerScope.h"
49 #include "mozilla/ipc/BackgroundUtils.h"
50 #include "mozilla/ipc/URIUtils.h"
51 #include "mozilla/net/CookieJarSettings.h"
52 #include "mozilla/PermissionManager.h"
54 mozilla::LazyLogModule gRemoteWorkerChildLog("RemoteWorkerChild");
56 #ifdef LOG
57 # undef LOG
58 #endif
59 #define LOG(fmt) MOZ_LOG(gRemoteWorkerChildLog, mozilla::LogLevel::Verbose, fmt)
61 namespace mozilla {
63 using namespace ipc;
65 namespace dom {
67 using workerinternals::ChannelFromScriptURLMainThread;
69 namespace {
71 class SharedWorkerInterfaceRequestor final : public nsIInterfaceRequestor {
72 public:
73 NS_DECL_ISUPPORTS
75 SharedWorkerInterfaceRequestor() {
76 // This check must match the code nsDocShell::Create.
77 if (XRE_IsParentProcess()) {
78 mSWController = new ServiceWorkerInterceptController();
82 NS_IMETHOD
83 GetInterface(const nsIID& aIID, void** aSink) override {
84 MOZ_ASSERT(NS_IsMainThread());
86 if (mSWController &&
87 aIID.Equals(NS_GET_IID(nsINetworkInterceptController))) {
88 // If asked for the network intercept controller, ask the outer requestor,
89 // which could be the docshell.
90 RefPtr<ServiceWorkerInterceptController> swController = mSWController;
91 swController.forget(aSink);
92 return NS_OK;
95 return NS_NOINTERFACE;
98 private:
99 ~SharedWorkerInterfaceRequestor() = default;
101 RefPtr<ServiceWorkerInterceptController> mSWController;
104 NS_IMPL_ADDREF(SharedWorkerInterfaceRequestor)
105 NS_IMPL_RELEASE(SharedWorkerInterfaceRequestor)
106 NS_IMPL_QUERY_INTERFACE(SharedWorkerInterfaceRequestor, nsIInterfaceRequestor)
108 // Normal runnable because AddPortIdentifier() is going to exec JS code.
109 class MessagePortIdentifierRunnable final : public WorkerRunnable {
110 public:
111 MessagePortIdentifierRunnable(WorkerPrivate* aWorkerPrivate,
112 RemoteWorkerChild* aActor,
113 const MessagePortIdentifier& aPortIdentifier)
114 : WorkerRunnable(aWorkerPrivate),
115 mActor(aActor),
116 mPortIdentifier(aPortIdentifier) {}
118 private:
119 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
120 if (aWorkerPrivate->GlobalScope()->IsDying()) {
121 mPortIdentifier.ForceClose();
122 return true;
124 mActor->AddPortIdentifier(aCx, aWorkerPrivate, mPortIdentifier);
125 return true;
128 RefPtr<RemoteWorkerChild> mActor;
129 UniqueMessagePortId mPortIdentifier;
132 } // anonymous namespace
134 RemoteWorkerChild::RemoteWorkerChild(const RemoteWorkerData& aData)
135 : mState(VariantType<Pending>(), "RemoteWorkerChild::mState"),
136 mServiceKeepAlive(RemoteWorkerService::MaybeGetKeepAlive()),
137 mIsServiceWorker(aData.serviceWorkerData().type() ==
138 OptionalServiceWorkerData::TServiceWorkerData) {
139 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
142 RemoteWorkerChild::~RemoteWorkerChild() {
143 #ifdef DEBUG
144 auto lock = mState.Lock();
145 MOZ_ASSERT(lock->is<Killed>());
146 #endif
149 void RemoteWorkerChild::ActorDestroy(ActorDestroyReason) {
150 auto launcherData = mLauncherData.Access();
152 Unused << NS_WARN_IF(!launcherData->mTerminationPromise.IsEmpty());
153 launcherData->mTerminationPromise.RejectIfExists(NS_ERROR_DOM_ABORT_ERR,
154 __func__);
156 auto lock = mState.Lock();
158 // If the worker hasn't shutdown or begun shutdown, we need to ensure it gets
159 // canceled.
160 if (NS_WARN_IF(!lock->is<Killed>() && !lock->is<Canceled>())) {
161 // In terms of strong references to this RemoteWorkerChild, at this moment:
162 // - IPC is holding a strong reference that will be dropped in the near
163 // future after this method returns.
164 // - If the worker has been started by ExecWorkerOnMainThread, then
165 // WorkerPrivate::mRemoteWorkerController is a strong reference to us.
166 // If the worker has not been started, ExecWorker's runnable lambda will
167 // have a strong reference that will cover the call to
168 // ExecWorkerOnMainThread.
169 // - The WorkerPrivate cancellation and termination callbacks will also
170 // hold strong references, but those callbacks will not outlive the
171 // WorkerPrivate and are not exposed to callers like
172 // mRemoteWorkerController is.
174 // Note that this call to RequestWorkerCancellation can still race worker
175 // cancellation, in which case the strong reference obtained by
176 // NewRunnableMethod can end up being the last strong reference.
177 // (RequestWorkerCancellation handles the case that the Worker is already
178 // canceled if this happens.)
179 RefPtr<nsIRunnable> runnable =
180 NewRunnableMethod("RequestWorkerCancellation", this,
181 &RemoteWorkerChild::RequestWorkerCancellation);
182 MOZ_ALWAYS_SUCCEEDS(
183 SchedulerGroup::Dispatch(TaskCategory::Other, runnable.forget()));
187 void RemoteWorkerChild::ExecWorker(const RemoteWorkerData& aData) {
188 #ifdef DEBUG
189 MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread());
190 auto launcherData = mLauncherData.Access();
191 MOZ_ASSERT(CanSend());
192 #endif
194 RefPtr<RemoteWorkerChild> self = this;
196 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
197 __func__, [self = std::move(self), data = aData]() mutable {
198 nsresult rv = self->ExecWorkerOnMainThread(std::move(data));
200 // Creation failure will already have been reported via the method
201 // above internally using ScopeExit.
202 Unused << NS_WARN_IF(NS_FAILED(rv));
205 MOZ_ALWAYS_SUCCEEDS(
206 SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
209 nsresult RemoteWorkerChild::ExecWorkerOnMainThread(RemoteWorkerData&& aData) {
210 MOZ_ASSERT(NS_IsMainThread());
212 // Ensure that the IndexedDatabaseManager is initialized so that if any
213 // workers do any IndexedDB calls that all of IDB's prefs/etc. are
214 // initialized.
215 Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
217 auto scopeExit =
218 MakeScopeExit([&] { ExceptionalErrorTransitionDuringExecWorker(); });
220 // Verify the the RemoteWorker should be really allowed to run in this
221 // process, and fail if it shouldn't (This shouldn't normally happen,
222 // unless the RemoteWorkerData has been tempered in the process it was
223 // sent from).
224 if (!RemoteWorkerManager::IsRemoteTypeAllowed(aData)) {
225 return NS_ERROR_UNEXPECTED;
228 auto principalOrErr = PrincipalInfoToPrincipal(aData.principalInfo());
229 if (NS_WARN_IF(principalOrErr.isErr())) {
230 return principalOrErr.unwrapErr();
233 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
235 auto loadingPrincipalOrErr =
236 PrincipalInfoToPrincipal(aData.loadingPrincipalInfo());
237 if (NS_WARN_IF(loadingPrincipalOrErr.isErr())) {
238 return loadingPrincipalOrErr.unwrapErr();
241 auto partitionedPrincipalOrErr =
242 PrincipalInfoToPrincipal(aData.partitionedPrincipalInfo());
243 if (NS_WARN_IF(partitionedPrincipalOrErr.isErr())) {
244 return partitionedPrincipalOrErr.unwrapErr();
247 WorkerLoadInfo info;
248 info.mBaseURI = DeserializeURI(aData.baseScriptURL());
249 info.mResolvedScriptURI = DeserializeURI(aData.resolvedScriptURL());
251 info.mPrincipalInfo = MakeUnique<PrincipalInfo>(aData.principalInfo());
252 info.mPartitionedPrincipalInfo =
253 MakeUnique<PrincipalInfo>(aData.partitionedPrincipalInfo());
255 info.mReferrerInfo = aData.referrerInfo();
256 info.mDomain = aData.domain();
257 info.mTrials = aData.originTrials();
258 info.mPrincipal = principal;
259 info.mPartitionedPrincipal = partitionedPrincipalOrErr.unwrap();
260 info.mLoadingPrincipal = loadingPrincipalOrErr.unwrap();
261 info.mStorageAccess = aData.storageAccess();
262 info.mUseRegularPrincipal = aData.useRegularPrincipal();
263 info.mHasStorageAccessPermissionGranted =
264 aData.hasStorageAccessPermissionGranted();
265 info.mIsThirdPartyContextToTopWindow = aData.isThirdPartyContextToTopWindow();
266 info.mOriginAttributes =
267 BasePrincipal::Cast(principal)->OriginAttributesRef();
268 info.mShouldResistFingerprinting = aData.shouldResistFingerprinting();
269 net::CookieJarSettings::Deserialize(aData.cookieJarSettings(),
270 getter_AddRefs(info.mCookieJarSettings));
271 info.mCookieJarSettingsArgs = aData.cookieJarSettings();
273 // Default CSP permissions for now. These will be overrided if necessary
274 // based on the script CSP headers during load in ScriptLoader.
275 info.mEvalAllowed = true;
276 info.mReportEvalCSPViolations = false;
277 info.mWasmEvalAllowed = true;
278 info.mReportWasmEvalCSPViolations = false;
279 info.mSecureContext = aData.isSecureContext()
280 ? WorkerLoadInfo::eSecureContext
281 : WorkerLoadInfo::eInsecureContext;
283 WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mLoadingPrincipal);
285 RefPtr<SharedWorkerInterfaceRequestor> requestor =
286 new SharedWorkerInterfaceRequestor();
287 info.mInterfaceRequestor->SetOuterRequestor(requestor);
289 Maybe<ClientInfo> clientInfo;
290 if (aData.clientInfo().isSome()) {
291 clientInfo.emplace(ClientInfo(aData.clientInfo().ref()));
294 nsresult rv = NS_OK;
296 if (clientInfo.isSome()) {
297 Maybe<mozilla::ipc::CSPInfo> cspInfo = clientInfo.ref().GetCspInfo();
298 if (cspInfo.isSome()) {
299 info.mCSP = CSPInfoToCSP(cspInfo.ref(), nullptr);
300 info.mCSPInfo = MakeUnique<CSPInfo>();
301 rv = CSPToCSPInfo(info.mCSP, info.mCSPInfo.get());
302 if (NS_WARN_IF(NS_FAILED(rv))) {
303 return rv;
308 rv = info.SetPrincipalsAndCSPOnMainThread(
309 info.mPrincipal, info.mPartitionedPrincipal, info.mLoadGroup, info.mCSP);
310 if (NS_WARN_IF(NS_FAILED(rv))) {
311 return rv;
314 nsString workerPrivateId;
316 if (mIsServiceWorker) {
317 ServiceWorkerData& data = aData.serviceWorkerData().get_ServiceWorkerData();
319 MOZ_ASSERT(!data.id().IsEmpty());
320 workerPrivateId = std::move(data.id());
322 info.mServiceWorkerCacheName = data.cacheName();
323 info.mServiceWorkerDescriptor.emplace(data.descriptor());
324 info.mServiceWorkerRegistrationDescriptor.emplace(
325 data.registrationDescriptor());
326 info.mLoadFlags = static_cast<nsLoadFlags>(data.loadFlags());
327 } else {
328 // Top level workers' main script use the document charset for the script
329 // uri encoding.
330 rv = ChannelFromScriptURLMainThread(
331 info.mLoadingPrincipal, nullptr /* parent document */, info.mLoadGroup,
332 info.mResolvedScriptURI, aData.type(), aData.credentials(), clientInfo,
333 nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER, info.mCookieJarSettings,
334 info.mReferrerInfo, getter_AddRefs(info.mChannel));
335 if (NS_WARN_IF(NS_FAILED(rv))) {
336 return rv;
340 info.mAgentClusterId = aData.agentClusterId();
342 AutoJSAPI jsapi;
343 jsapi.Init();
345 ErrorResult error;
346 RefPtr<RemoteWorkerChild> self = this;
347 RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
348 jsapi.cx(), aData.originalScriptURL(), false,
349 mIsServiceWorker ? WorkerKindService : WorkerKindShared,
350 aData.credentials(), aData.type(), aData.name(), VoidCString(), &info,
351 error, std::move(workerPrivateId),
352 [self](bool aEverRan) {
353 self->OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled();
355 // This will be invoked here on the main thread when the worker is already
356 // fully shutdown. This replaces a prior approach where we would
357 // begin to transition when the worker thread would reach the Canceling
358 // state. This lambda ensures that we not only wait for the Killing state
359 // to be reached but that the global shutdown has already occurred.
360 [self]() { self->TransitionStateFromCanceledToKilled(); });
362 if (NS_WARN_IF(error.Failed())) {
363 MOZ_ASSERT(!workerPrivate);
365 rv = error.StealNSResult();
366 return rv;
369 workerPrivate->SetRemoteWorkerController(this);
371 // This wants to run as a normal task sequentially after the top level script
372 // evaluation, so the hybrid target is the correct choice between hybrid and
373 // `ControlEventTarget`.
374 nsCOMPtr<nsISerialEventTarget> workerTarget =
375 workerPrivate->HybridEventTarget();
377 nsCOMPtr<nsIRunnable> runnable = NewCancelableRunnableMethod(
378 "InitialzeOnWorker", this, &RemoteWorkerChild::InitializeOnWorker);
381 MOZ_ASSERT(workerPrivate);
382 auto lock = mState.Lock();
383 // We MUST be pending here, so direct access is ok.
384 lock->as<Pending>().mWorkerPrivate = std::move(workerPrivate);
387 if (mIsServiceWorker) {
388 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
389 __func__, [workerTarget,
390 initializeWorkerRunnable = std::move(runnable)]() mutable {
391 Unused << NS_WARN_IF(NS_FAILED(
392 workerTarget->Dispatch(initializeWorkerRunnable.forget())));
395 RefPtr<PermissionManager> permissionManager =
396 PermissionManager::GetInstance();
397 if (!permissionManager) {
398 return NS_ERROR_FAILURE;
400 permissionManager->WhenPermissionsAvailable(principal, r);
401 } else {
402 if (NS_WARN_IF(NS_FAILED(workerTarget->Dispatch(runnable.forget())))) {
403 rv = NS_ERROR_FAILURE;
404 return rv;
408 scopeExit.release();
410 return NS_OK;
413 void RemoteWorkerChild::RequestWorkerCancellation() {
414 MOZ_ASSERT(NS_IsMainThread());
416 LOG(("RequestWorkerCancellation[this=%p]", this));
418 // We want to ensure that we've requested the worker be canceled. So if the
419 // worker is running, cancel it. We can't do this with the lock held,
420 // however, because our lambdas will want to manipulate the state.
421 RefPtr<WorkerPrivate> cancelWith;
423 auto lock = mState.Lock();
424 if (lock->is<Pending>()) {
425 cancelWith = lock->as<Pending>().mWorkerPrivate;
426 } else if (lock->is<Running>()) {
427 cancelWith = lock->as<Running>().mWorkerPrivate;
431 if (cancelWith) {
432 cancelWith->Cancel();
436 // This method will be invoked on the worker after the top-level
437 // CompileScriptRunnable task has succeeded and as long as the worker has not
438 // been closed/canceled. There are edge-cases related to cancellation, but we
439 // have our caller ensure that we are only called as long as the worker's state
440 // is Running.
442 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1800659 will eliminate
443 // cancellation, and the documentation around that bug / in design documents
444 // helps provide more context about this.)
445 void RemoteWorkerChild::InitializeOnWorker() {
446 nsCOMPtr<nsIRunnable> r =
447 NewRunnableMethod("TransitionStateToRunning", this,
448 &RemoteWorkerChild::TransitionStateToRunning);
449 MOZ_ALWAYS_SUCCEEDS(
450 SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
453 RefPtr<GenericNonExclusivePromise> RemoteWorkerChild::GetTerminationPromise() {
454 auto launcherData = mLauncherData.Access();
455 return launcherData->mTerminationPromise.Ensure(__func__);
458 void RemoteWorkerChild::CreationSucceededOnAnyThread() {
459 CreationSucceededOrFailedOnAnyThread(true);
462 void RemoteWorkerChild::CreationFailedOnAnyThread() {
463 CreationSucceededOrFailedOnAnyThread(false);
466 void RemoteWorkerChild::CreationSucceededOrFailedOnAnyThread(
467 bool aDidCreationSucceed) {
468 #ifdef DEBUG
470 auto lock = mState.Lock();
471 MOZ_ASSERT_IF(aDidCreationSucceed, lock->is<Running>());
472 MOZ_ASSERT_IF(!aDidCreationSucceed, lock->is<Killed>());
474 #endif
476 RefPtr<RemoteWorkerChild> self = this;
478 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
479 __func__,
480 [self = std::move(self), didCreationSucceed = aDidCreationSucceed] {
481 auto launcherData = self->mLauncherData.Access();
483 if (!self->CanSend() || launcherData->mDidSendCreated) {
484 return;
487 Unused << self->SendCreated(didCreationSucceed);
488 launcherData->mDidSendCreated = true;
491 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
494 void RemoteWorkerChild::CloseWorkerOnMainThread() {
495 AssertIsOnMainThread();
497 LOG(("CloseWorkerOnMainThread[this=%p]", this));
499 // We can't hold the state lock while calling WorkerPrivate::Cancel because
500 // the lambda callback will want to touch the state, so save off the
501 // WorkerPrivate so we can cancel it (if we need to cancel it).
502 RefPtr<WorkerPrivate> cancelWith;
504 auto lock = mState.Lock();
506 if (lock->is<Pending>()) {
507 cancelWith = lock->as<Pending>().mWorkerPrivate;
508 // There should be no way for this code to run before we
509 // ExecWorkerOnMainThread runs, which means that either it should have
510 // set a WorkerPrivate on Pending, or its error handling should already
511 // have transitioned us to Canceled and Killing in that order. (It's
512 // also possible that it assigned a WorkerPrivate and subsequently we
513 // transitioned to Running, which would put us in the next branch.)
514 MOZ_DIAGNOSTIC_ASSERT(cancelWith);
515 } else if (lock->is<Running>()) {
516 cancelWith = lock->as<Running>().mWorkerPrivate;
520 // It's very okay for us to not have a WorkerPrivate here if we've already
521 // canceled the worker or if errors happened.
522 if (cancelWith) {
523 cancelWith->Cancel();
528 * Error reporting method
530 void RemoteWorkerChild::ErrorPropagation(const ErrorValue& aValue) {
531 MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread());
533 if (!CanSend()) {
534 return;
537 Unused << SendError(aValue);
540 void RemoteWorkerChild::ErrorPropagationDispatch(nsresult aError) {
541 MOZ_ASSERT(NS_FAILED(aError));
543 RefPtr<RemoteWorkerChild> self = this;
544 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
545 "RemoteWorkerChild::ErrorPropagationDispatch",
546 [self = std::move(self), aError]() { self->ErrorPropagation(aError); });
548 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
551 void RemoteWorkerChild::ErrorPropagationOnMainThread(
552 const WorkerErrorReport* aReport, bool aIsErrorEvent) {
553 AssertIsOnMainThread();
555 ErrorValue value;
556 if (aIsErrorEvent) {
557 ErrorData data(
558 aReport->mIsWarning, aReport->mLineNumber, aReport->mColumnNumber,
559 aReport->mMessage, aReport->mFilename, aReport->mLine,
560 TransformIntoNewArray(aReport->mNotes, [](const WorkerErrorNote& note) {
561 return ErrorDataNote(note.mLineNumber, note.mColumnNumber,
562 note.mMessage, note.mFilename);
563 }));
564 value = data;
565 } else {
566 value = void_t();
569 RefPtr<RemoteWorkerChild> self = this;
570 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
571 "RemoteWorkerChild::ErrorPropagationOnMainThread",
572 [self = std::move(self), value]() { self->ErrorPropagation(value); });
574 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
577 void RemoteWorkerChild::NotifyLock(bool aCreated) {
578 nsCOMPtr<nsIRunnable> r =
579 NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] {
580 if (!self->CanSend()) {
581 return;
584 Unused << self->SendNotifyLock(aCreated);
587 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
590 void RemoteWorkerChild::NotifyWebTransport(bool aCreated) {
591 nsCOMPtr<nsIRunnable> r =
592 NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] {
593 if (!self->CanSend()) {
594 return;
597 Unused << self->SendNotifyWebTransport(aCreated);
600 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
603 void RemoteWorkerChild::FlushReportsOnMainThread(
604 nsIConsoleReportCollector* aReporter) {
605 AssertIsOnMainThread();
607 bool reportErrorToBrowserConsole = true;
609 // Flush the reports.
610 for (uint32_t i = 0, len = mWindowIDs.Length(); i < len; ++i) {
611 aReporter->FlushReportsToConsole(
612 mWindowIDs[i], nsIConsoleReportCollector::ReportAction::Save);
613 reportErrorToBrowserConsole = false;
616 // Finally report to browser console if there is no any window.
617 if (reportErrorToBrowserConsole) {
618 aReporter->FlushReportsToConsole(0);
619 return;
622 aReporter->ClearConsoleReports();
626 * Worker state transition methods
628 RemoteWorkerChild::WorkerPrivateAccessibleState::
629 ~WorkerPrivateAccessibleState() {
630 // We should now only be performing state transitions on the main thread, so
631 // we should assert we're only releasing on the main thread.
632 MOZ_ASSERT(!mWorkerPrivate || NS_IsMainThread());
633 // mWorkerPrivate can be safely released on the main thread.
634 if (!mWorkerPrivate || NS_IsMainThread()) {
635 return;
638 // But as a backstop, do proxy the release to the main thread.
639 NS_ReleaseOnMainThread(
640 "RemoteWorkerChild::WorkerPrivateAccessibleState::mWorkerPrivate",
641 mWorkerPrivate.forget());
644 void RemoteWorkerChild::
645 OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled() {
646 auto lock = mState.Lock();
648 LOG(("TransitionStateFromPendingOrRunningToCanceled[this=%p]", this));
650 if (lock->is<Pending>()) {
651 TransitionStateFromPendingToCanceled(lock.ref());
652 } else if (lock->is<Running>()) {
653 *lock = VariantType<Canceled>();
654 } else {
655 MOZ_ASSERT(false, "State should have been Pending or Running");
659 void RemoteWorkerChild::TransitionStateFromPendingToCanceled(State& aState) {
660 AssertIsOnMainThread();
661 MOZ_ASSERT(aState.is<Pending>());
662 LOG(("TransitionStateFromPendingToCanceled[this=%p]", this));
664 CancelAllPendingOps(aState);
666 aState = VariantType<Canceled>();
669 void RemoteWorkerChild::TransitionStateFromCanceledToKilled() {
670 AssertIsOnMainThread();
672 LOG(("TransitionStateFromCanceledToKilled[this=%p]", this));
674 auto lock = mState.Lock();
675 MOZ_ASSERT(lock->is<Canceled>());
677 *lock = VariantType<Killed>();
679 RefPtr<RemoteWorkerChild> self = this;
680 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() {
681 auto launcherData = self->mLauncherData.Access();
683 // (We maintain the historical ordering of resolving this promise prior to
684 // calling SendClose, however the previous code used 2 separate dispatches
685 // to this thread for the resolve and SendClose, and there inherently
686 // would be a race between the runnables resulting from the resolved
687 // promise and the promise containing the call to SendClose. Now it's
688 // entirely clear that our call to SendClose will effectively run before
689 // any of the resolved promises are able to do anything.)
690 launcherData->mTerminationPromise.ResolveIfExists(true, __func__);
692 if (self->CanSend()) {
693 Unused << self->SendClose();
697 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
700 void RemoteWorkerChild::TransitionStateToRunning() {
701 AssertIsOnMainThread();
703 LOG(("TransitionStateToRunning[this=%p]", this));
705 nsTArray<RefPtr<Op>> pendingOps;
708 auto lock = mState.Lock();
710 // Because this is an async notification sent from the worker to the main
711 // thread, it's very possible that we've already decided on the main thread
712 // to transition to the Canceled state, in which case there is nothing for
713 // us to do here.
714 if (!lock->is<Pending>()) {
715 LOG(("State is already not pending in TransitionStateToRunning[this=%p]!",
716 this));
717 return;
720 RefPtr<WorkerPrivate> workerPrivate =
721 std::move(lock->as<Pending>().mWorkerPrivate);
722 pendingOps = std::move(lock->as<Pending>().mPendingOps);
724 // Move the worker private into place to avoid gratuitous ref churn; prior
725 // comments here suggest the Variant can't accept a move.
726 *lock = VariantType<Running>();
727 lock->as<Running>().mWorkerPrivate = std::move(workerPrivate);
730 CreationSucceededOnAnyThread();
732 RefPtr<RemoteWorkerChild> self = this;
733 for (auto& op : pendingOps) {
734 op->StartOnMainThread(self);
738 void RemoteWorkerChild::ExceptionalErrorTransitionDuringExecWorker() {
739 AssertIsOnMainThread();
741 LOG(("ExceptionalErrorTransitionDuringExecWorker[this=%p]", this));
743 // This method is called synchronously by ExecWorkerOnMainThread in the event
744 // of any error. Because we only transition to Running on the main thread
745 // as the result of a notification from the worker, we know our state will be
746 // Pending, but mWorkerPrivate may or may not be null, as we may not have
747 // gotten to spawning the worker.
749 // In the event the worker exists, we need to Cancel() it. We must do this
750 // without the lock held because our call to Cancel() will invoke the
751 // cancellation callback we created which will call TransitionStateToCanceled,
752 // and we can't be holding the lock when that happens.
754 RefPtr<WorkerPrivate> cancelWith;
757 auto lock = mState.Lock();
759 MOZ_ASSERT(lock->is<Pending>());
760 if (lock->is<Pending>()) {
761 cancelWith = lock->as<Pending>().mWorkerPrivate;
762 if (!cancelWith) {
763 // The worker wasn't actually created, so we should synthetically
764 // transition to canceled and onward. Since we have the lock,
765 // perform the transition now for clarity, but we'll handle the rest of
766 // this case after dropping the lock.
767 TransitionStateFromPendingToCanceled(lock.ref());
772 if (cancelWith) {
773 cancelWith->Cancel();
774 } else {
775 TransitionStateFromCanceledToKilled();
776 CreationFailedOnAnyThread();
781 * Operation execution classes/methods
783 class RemoteWorkerChild::SharedWorkerOp : public RemoteWorkerChild::Op {
784 public:
785 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerOp, override)
787 explicit SharedWorkerOp(RemoteWorkerOp&& aOp) : mOp(std::move(aOp)) {}
789 bool MaybeStart(RemoteWorkerChild* aOwner,
790 RemoteWorkerChild::State& aState) override {
791 MOZ_ASSERT(!mStarted);
792 MOZ_ASSERT(aOwner);
793 // Thread: We are on the Worker Launcher thread.
795 // Return false, indicating we should queue this op if our current state is
796 // pending and this isn't a termination op (which should skip the line).
797 if (aState.is<Pending>() && !IsTerminationOp()) {
798 return false;
801 // If the worker is already shutting down (which should be unexpected
802 // because we should be told new operations after a termination op), just
803 // return true to indicate the op should be discarded.
804 if (aState.is<Canceled>() || aState.is<Killed>()) {
805 #ifdef DEBUG
806 mStarted = true;
807 #endif
808 if (mOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
809 MessagePort::ForceClose(
810 mOp.get_RemoteWorkerPortIdentifierOp().portIdentifier());
812 return true;
815 MOZ_ASSERT(aState.is<Running>() || IsTerminationOp());
817 RefPtr<SharedWorkerOp> self = this;
818 RefPtr<RemoteWorkerChild> owner = aOwner;
820 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
821 __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
823 auto lock = owner->mState.Lock();
825 if (NS_WARN_IF(lock->is<Canceled>() || lock->is<Killed>())) {
826 self->Cancel();
827 // Worker has already canceled, force close the MessagePort.
828 if (self->mOp.type() ==
829 RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
830 MessagePort::ForceClose(
831 self->mOp.get_RemoteWorkerPortIdentifierOp()
832 .portIdentifier());
834 return;
838 self->StartOnMainThread(owner);
841 MOZ_ALWAYS_SUCCEEDS(
842 SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
844 #ifdef DEBUG
845 mStarted = true;
846 #endif
848 return true;
851 void StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) final {
852 using Running = RemoteWorkerChild::Running;
854 AssertIsOnMainThread();
856 if (IsTerminationOp()) {
857 aOwner->CloseWorkerOnMainThread();
858 return;
861 auto lock = aOwner->mState.Lock();
862 MOZ_ASSERT(lock->is<Running>());
863 if (!lock->is<Running>()) {
864 aOwner->ErrorPropagationDispatch(NS_ERROR_DOM_INVALID_STATE_ERR);
865 return;
868 RefPtr<WorkerPrivate> workerPrivate = lock->as<Running>().mWorkerPrivate;
870 MOZ_ASSERT(workerPrivate);
872 if (mOp.type() == RemoteWorkerOp::TRemoteWorkerSuspendOp) {
873 workerPrivate->ParentWindowPaused();
874 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerResumeOp) {
875 workerPrivate->ParentWindowResumed();
876 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerFreezeOp) {
877 workerPrivate->Freeze(nullptr);
878 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerThawOp) {
879 workerPrivate->Thaw(nullptr);
880 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
881 RefPtr<MessagePortIdentifierRunnable> r =
882 new MessagePortIdentifierRunnable(
883 workerPrivate, aOwner,
884 mOp.get_RemoteWorkerPortIdentifierOp().portIdentifier());
885 if (NS_WARN_IF(!r->Dispatch())) {
886 aOwner->ErrorPropagationDispatch(NS_ERROR_FAILURE);
888 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerAddWindowIDOp) {
889 aOwner->mWindowIDs.AppendElement(
890 mOp.get_RemoteWorkerAddWindowIDOp().windowID());
891 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerRemoveWindowIDOp) {
892 aOwner->mWindowIDs.RemoveElement(
893 mOp.get_RemoteWorkerRemoveWindowIDOp().windowID());
894 } else {
895 MOZ_CRASH("Unknown RemoteWorkerOp type!");
899 void Cancel() override {
900 #ifdef DEBUG
901 mStarted = true;
902 #endif
905 private:
906 ~SharedWorkerOp() { MOZ_ASSERT(mStarted); }
908 bool IsTerminationOp() const {
909 return mOp.type() == RemoteWorkerOp::TRemoteWorkerTerminateOp;
912 RemoteWorkerOp mOp;
914 #ifdef DEBUG
915 bool mStarted = false;
916 #endif
919 void RemoteWorkerChild::AddPortIdentifier(
920 JSContext* aCx, WorkerPrivate* aWorkerPrivate,
921 UniqueMessagePortId& aPortIdentifier) {
922 if (NS_WARN_IF(!aWorkerPrivate->ConnectMessagePort(aCx, aPortIdentifier))) {
923 ErrorPropagationDispatch(NS_ERROR_FAILURE);
927 void RemoteWorkerChild::CancelAllPendingOps(State& aState) {
928 MOZ_ASSERT(aState.is<Pending>());
930 auto pendingOps = std::move(aState.as<Pending>().mPendingOps);
932 for (auto& op : pendingOps) {
933 op->Cancel();
937 void RemoteWorkerChild::MaybeStartOp(RefPtr<Op>&& aOp) {
938 MOZ_ASSERT(aOp);
940 auto lock = mState.Lock();
942 if (!aOp->MaybeStart(this, lock.ref())) {
943 // Maybestart returns false only if we are <Pending>.
944 lock->as<Pending>().mPendingOps.AppendElement(std::move(aOp));
948 IPCResult RemoteWorkerChild::RecvExecOp(RemoteWorkerOp&& aOp) {
949 MOZ_ASSERT(!mIsServiceWorker);
951 MaybeStartOp(new SharedWorkerOp(std::move(aOp)));
953 return IPC_OK();
956 IPCResult RemoteWorkerChild::RecvExecServiceWorkerOp(
957 ServiceWorkerOpArgs&& aArgs, ExecServiceWorkerOpResolver&& aResolve) {
958 MOZ_ASSERT(mIsServiceWorker);
959 MOZ_ASSERT(
960 aArgs.type() !=
961 ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs,
962 "FetchEvent operations should be sent via PFetchEventOp(Proxy) actors!");
964 MaybeReportServiceWorkerShutdownProgress(aArgs);
966 MaybeStartOp(ServiceWorkerOp::Create(std::move(aArgs), std::move(aResolve)));
968 return IPC_OK();
971 RefPtr<GenericPromise>
972 RemoteWorkerChild::MaybeSendSetServiceWorkerSkipWaitingFlag() {
973 RefPtr<GenericPromise::Private> promise =
974 new GenericPromise::Private(__func__);
976 RefPtr<RemoteWorkerChild> self = this;
978 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self = std::move(
979 self),
980 promise] {
981 if (!self->CanSend()) {
982 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
983 return;
986 self->SendSetServiceWorkerSkipWaitingFlag()->Then(
987 GetCurrentSerialEventTarget(), __func__,
988 [promise](
989 const SetServiceWorkerSkipWaitingFlagPromise::ResolveOrRejectValue&
990 aResult) {
991 if (NS_WARN_IF(aResult.IsReject())) {
992 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
993 return;
996 promise->Resolve(aResult.ResolveValue(), __func__);
1000 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
1002 return promise;
1006 * PFetchEventOpProxy methods
1008 already_AddRefed<PFetchEventOpProxyChild>
1009 RemoteWorkerChild::AllocPFetchEventOpProxyChild(
1010 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
1011 return RefPtr{new FetchEventOpProxyChild()}.forget();
1014 IPCResult RemoteWorkerChild::RecvPFetchEventOpProxyConstructor(
1015 PFetchEventOpProxyChild* aActor,
1016 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
1017 MOZ_ASSERT(aActor);
1019 (static_cast<FetchEventOpProxyChild*>(aActor))->Initialize(aArgs);
1021 return IPC_OK();
1024 } // namespace dom
1025 } // namespace mozilla