Backed out 6 changesets (bug 1876574, bug 1876575) for causing multiple failures...
[gecko.git] / dom / workers / remoteworkers / RemoteWorkerChild.cpp
blobfeb294f3fc2be22abee6a4334e8c4a9baac9a0b6
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, "MessagePortIdentifierRunnable"),
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 // This is used to propagate the CSP violation when loading the SharedWorker
133 // main-script and nothing else.
134 class RemoteWorkerCSPEventListener final : public nsICSPEventListener {
135 public:
136 NS_DECL_ISUPPORTS
138 explicit RemoteWorkerCSPEventListener(RemoteWorkerChild* aActor)
139 : mActor(aActor){};
141 NS_IMETHOD OnCSPViolationEvent(const nsAString& aJSON) override {
142 mActor->CSPViolationPropagationOnMainThread(aJSON);
143 return NS_OK;
146 private:
147 ~RemoteWorkerCSPEventListener() = default;
149 RefPtr<RemoteWorkerChild> mActor;
152 NS_IMPL_ISUPPORTS(RemoteWorkerCSPEventListener, nsICSPEventListener)
154 } // anonymous namespace
156 RemoteWorkerChild::RemoteWorkerChild(const RemoteWorkerData& aData)
157 : mState(VariantType<Pending>(), "RemoteWorkerChild::mState"),
158 mServiceKeepAlive(RemoteWorkerService::MaybeGetKeepAlive()),
159 mIsServiceWorker(aData.serviceWorkerData().type() ==
160 OptionalServiceWorkerData::TServiceWorkerData) {
161 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
164 RemoteWorkerChild::~RemoteWorkerChild() {
165 #ifdef DEBUG
166 auto lock = mState.Lock();
167 MOZ_ASSERT(lock->is<Killed>());
168 #endif
171 void RemoteWorkerChild::ActorDestroy(ActorDestroyReason) {
172 auto launcherData = mLauncherData.Access();
174 Unused << NS_WARN_IF(!launcherData->mTerminationPromise.IsEmpty());
175 launcherData->mTerminationPromise.RejectIfExists(NS_ERROR_DOM_ABORT_ERR,
176 __func__);
178 auto lock = mState.Lock();
180 // If the worker hasn't shutdown or begun shutdown, we need to ensure it gets
181 // canceled.
182 if (NS_WARN_IF(!lock->is<Killed>() && !lock->is<Canceled>())) {
183 // In terms of strong references to this RemoteWorkerChild, at this moment:
184 // - IPC is holding a strong reference that will be dropped in the near
185 // future after this method returns.
186 // - If the worker has been started by ExecWorkerOnMainThread, then
187 // WorkerPrivate::mRemoteWorkerController is a strong reference to us.
188 // If the worker has not been started, ExecWorker's runnable lambda will
189 // have a strong reference that will cover the call to
190 // ExecWorkerOnMainThread.
191 // - The WorkerPrivate cancellation and termination callbacks will also
192 // hold strong references, but those callbacks will not outlive the
193 // WorkerPrivate and are not exposed to callers like
194 // mRemoteWorkerController is.
196 // Note that this call to RequestWorkerCancellation can still race worker
197 // cancellation, in which case the strong reference obtained by
198 // NewRunnableMethod can end up being the last strong reference.
199 // (RequestWorkerCancellation handles the case that the Worker is already
200 // canceled if this happens.)
201 RefPtr<nsIRunnable> runnable =
202 NewRunnableMethod("RequestWorkerCancellation", this,
203 &RemoteWorkerChild::RequestWorkerCancellation);
204 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(runnable.forget()));
208 void RemoteWorkerChild::ExecWorker(const RemoteWorkerData& aData) {
209 #ifdef DEBUG
210 MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread());
211 auto launcherData = mLauncherData.Access();
212 MOZ_ASSERT(CanSend());
213 #endif
215 RefPtr<RemoteWorkerChild> self = this;
217 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
218 __func__, [self = std::move(self), data = aData]() mutable {
219 nsresult rv = self->ExecWorkerOnMainThread(std::move(data));
221 // Creation failure will already have been reported via the method
222 // above internally using ScopeExit.
223 Unused << NS_WARN_IF(NS_FAILED(rv));
226 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
229 nsresult RemoteWorkerChild::ExecWorkerOnMainThread(RemoteWorkerData&& aData) {
230 MOZ_ASSERT(NS_IsMainThread());
232 // Ensure that the IndexedDatabaseManager is initialized so that if any
233 // workers do any IndexedDB calls that all of IDB's prefs/etc. are
234 // initialized.
235 IndexedDatabaseManager* idm = IndexedDatabaseManager::GetOrCreate();
236 if (idm) {
237 Unused << NS_WARN_IF(NS_FAILED(idm->EnsureLocale()));
238 } else {
239 NS_WARNING("Failed to get IndexedDatabaseManager!");
242 auto scopeExit =
243 MakeScopeExit([&] { ExceptionalErrorTransitionDuringExecWorker(); });
245 // Verify the the RemoteWorker should be really allowed to run in this
246 // process, and fail if it shouldn't (This shouldn't normally happen,
247 // unless the RemoteWorkerData has been tempered in the process it was
248 // sent from).
249 if (!RemoteWorkerManager::IsRemoteTypeAllowed(aData)) {
250 return NS_ERROR_UNEXPECTED;
253 auto principalOrErr = PrincipalInfoToPrincipal(aData.principalInfo());
254 if (NS_WARN_IF(principalOrErr.isErr())) {
255 return principalOrErr.unwrapErr();
258 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
260 auto loadingPrincipalOrErr =
261 PrincipalInfoToPrincipal(aData.loadingPrincipalInfo());
262 if (NS_WARN_IF(loadingPrincipalOrErr.isErr())) {
263 return loadingPrincipalOrErr.unwrapErr();
266 auto partitionedPrincipalOrErr =
267 PrincipalInfoToPrincipal(aData.partitionedPrincipalInfo());
268 if (NS_WARN_IF(partitionedPrincipalOrErr.isErr())) {
269 return partitionedPrincipalOrErr.unwrapErr();
272 WorkerLoadInfo info;
273 info.mBaseURI = DeserializeURI(aData.baseScriptURL());
274 info.mResolvedScriptURI = DeserializeURI(aData.resolvedScriptURL());
276 info.mPrincipalInfo = MakeUnique<PrincipalInfo>(aData.principalInfo());
277 info.mPartitionedPrincipalInfo =
278 MakeUnique<PrincipalInfo>(aData.partitionedPrincipalInfo());
280 info.mReferrerInfo = aData.referrerInfo();
281 info.mDomain = aData.domain();
282 info.mTrials = aData.originTrials();
283 info.mPrincipal = principal;
284 info.mPartitionedPrincipal = partitionedPrincipalOrErr.unwrap();
285 info.mLoadingPrincipal = loadingPrincipalOrErr.unwrap();
286 info.mStorageAccess = aData.storageAccess();
287 info.mUseRegularPrincipal = aData.useRegularPrincipal();
288 info.mUsingStorageAccess = aData.usingStorageAccess();
289 info.mIsThirdPartyContextToTopWindow = aData.isThirdPartyContextToTopWindow();
290 info.mOriginAttributes =
291 BasePrincipal::Cast(principal)->OriginAttributesRef();
292 info.mShouldResistFingerprinting = aData.shouldResistFingerprinting();
293 Maybe<RFPTarget> overriddenFingerprintingSettings;
294 if (aData.overriddenFingerprintingSettings().isSome()) {
295 overriddenFingerprintingSettings.emplace(
296 RFPTarget(aData.overriddenFingerprintingSettings().ref()));
298 info.mOverriddenFingerprintingSettings = overriddenFingerprintingSettings;
299 net::CookieJarSettings::Deserialize(aData.cookieJarSettings(),
300 getter_AddRefs(info.mCookieJarSettings));
301 info.mCookieJarSettingsArgs = aData.cookieJarSettings();
303 // Default CSP permissions for now. These will be overrided if necessary
304 // based on the script CSP headers during load in ScriptLoader.
305 info.mEvalAllowed = true;
306 info.mReportEvalCSPViolations = false;
307 info.mWasmEvalAllowed = true;
308 info.mReportWasmEvalCSPViolations = false;
309 info.mSecureContext = aData.isSecureContext()
310 ? WorkerLoadInfo::eSecureContext
311 : WorkerLoadInfo::eInsecureContext;
313 WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mLoadingPrincipal);
315 RefPtr<SharedWorkerInterfaceRequestor> requestor =
316 new SharedWorkerInterfaceRequestor();
317 info.mInterfaceRequestor->SetOuterRequestor(requestor);
319 Maybe<ClientInfo> clientInfo;
320 if (aData.clientInfo().isSome()) {
321 clientInfo.emplace(ClientInfo(aData.clientInfo().ref()));
324 nsresult rv = NS_OK;
326 if (clientInfo.isSome()) {
327 Maybe<mozilla::ipc::CSPInfo> cspInfo = clientInfo.ref().GetCspInfo();
328 if (cspInfo.isSome()) {
329 info.mCSP = CSPInfoToCSP(cspInfo.ref(), nullptr);
330 info.mCSPInfo = MakeUnique<CSPInfo>();
331 rv = CSPToCSPInfo(info.mCSP, info.mCSPInfo.get());
332 if (NS_WARN_IF(NS_FAILED(rv))) {
333 return rv;
338 rv = info.SetPrincipalsAndCSPOnMainThread(
339 info.mPrincipal, info.mPartitionedPrincipal, info.mLoadGroup, info.mCSP);
340 if (NS_WARN_IF(NS_FAILED(rv))) {
341 return rv;
344 nsString workerPrivateId;
346 if (mIsServiceWorker) {
347 ServiceWorkerData& data = aData.serviceWorkerData().get_ServiceWorkerData();
349 MOZ_ASSERT(!data.id().IsEmpty());
350 workerPrivateId = std::move(data.id());
352 info.mServiceWorkerCacheName = data.cacheName();
353 info.mServiceWorkerDescriptor.emplace(data.descriptor());
354 info.mServiceWorkerRegistrationDescriptor.emplace(
355 data.registrationDescriptor());
356 info.mLoadFlags = static_cast<nsLoadFlags>(data.loadFlags());
357 } else {
358 // Top level workers' main script use the document charset for the script
359 // uri encoding.
360 rv = ChannelFromScriptURLMainThread(
361 info.mLoadingPrincipal, nullptr /* parent document */, info.mLoadGroup,
362 info.mResolvedScriptURI, aData.type(), aData.credentials(), clientInfo,
363 nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER, info.mCookieJarSettings,
364 info.mReferrerInfo, getter_AddRefs(info.mChannel));
365 if (NS_WARN_IF(NS_FAILED(rv))) {
366 return rv;
369 nsCOMPtr<nsILoadInfo> loadInfo = info.mChannel->LoadInfo();
371 auto* cspEventListener = new RemoteWorkerCSPEventListener(this);
372 rv = loadInfo->SetCspEventListener(cspEventListener);
373 if (NS_WARN_IF(NS_FAILED(rv))) {
374 return rv;
378 info.mAgentClusterId = aData.agentClusterId();
380 AutoJSAPI jsapi;
381 jsapi.Init();
383 ErrorResult error;
384 RefPtr<RemoteWorkerChild> self = this;
385 RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
386 jsapi.cx(), aData.originalScriptURL(), false,
387 mIsServiceWorker ? WorkerKindService : WorkerKindShared,
388 aData.credentials(), aData.type(), aData.name(), VoidCString(), &info,
389 error, std::move(workerPrivateId),
390 [self](bool aEverRan) {
391 self->OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled();
393 // This will be invoked here on the main thread when the worker is already
394 // fully shutdown. This replaces a prior approach where we would
395 // begin to transition when the worker thread would reach the Canceling
396 // state. This lambda ensures that we not only wait for the Killing state
397 // to be reached but that the global shutdown has already occurred.
398 [self]() { self->TransitionStateFromCanceledToKilled(); });
400 if (NS_WARN_IF(error.Failed())) {
401 MOZ_ASSERT(!workerPrivate);
403 rv = error.StealNSResult();
404 return rv;
407 workerPrivate->SetRemoteWorkerController(this);
409 // This wants to run as a normal task sequentially after the top level script
410 // evaluation, so the hybrid target is the correct choice between hybrid and
411 // `ControlEventTarget`.
412 nsCOMPtr<nsISerialEventTarget> workerTarget =
413 workerPrivate->HybridEventTarget();
415 nsCOMPtr<nsIRunnable> runnable = NewCancelableRunnableMethod(
416 "InitialzeOnWorker", this, &RemoteWorkerChild::InitializeOnWorker);
419 MOZ_ASSERT(workerPrivate);
420 auto lock = mState.Lock();
421 // We MUST be pending here, so direct access is ok.
422 lock->as<Pending>().mWorkerPrivate = std::move(workerPrivate);
425 if (mIsServiceWorker) {
426 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
427 __func__, [workerTarget,
428 initializeWorkerRunnable = std::move(runnable)]() mutable {
429 Unused << NS_WARN_IF(NS_FAILED(
430 workerTarget->Dispatch(initializeWorkerRunnable.forget())));
433 RefPtr<PermissionManager> permissionManager =
434 PermissionManager::GetInstance();
435 if (!permissionManager) {
436 return NS_ERROR_FAILURE;
438 permissionManager->WhenPermissionsAvailable(principal, r);
439 } else {
440 if (NS_WARN_IF(NS_FAILED(workerTarget->Dispatch(runnable.forget())))) {
441 rv = NS_ERROR_FAILURE;
442 return rv;
446 scopeExit.release();
448 return NS_OK;
451 void RemoteWorkerChild::RequestWorkerCancellation() {
452 MOZ_ASSERT(NS_IsMainThread());
454 LOG(("RequestWorkerCancellation[this=%p]", this));
456 // We want to ensure that we've requested the worker be canceled. So if the
457 // worker is running, cancel it. We can't do this with the lock held,
458 // however, because our lambdas will want to manipulate the state.
459 RefPtr<WorkerPrivate> cancelWith;
461 auto lock = mState.Lock();
462 if (lock->is<Pending>()) {
463 cancelWith = lock->as<Pending>().mWorkerPrivate;
464 } else if (lock->is<Running>()) {
465 cancelWith = lock->as<Running>().mWorkerPrivate;
469 if (cancelWith) {
470 cancelWith->Cancel();
474 // This method will be invoked on the worker after the top-level
475 // CompileScriptRunnable task has succeeded and as long as the worker has not
476 // been closed/canceled. There are edge-cases related to cancellation, but we
477 // have our caller ensure that we are only called as long as the worker's state
478 // is Running.
480 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1800659 will eliminate
481 // cancellation, and the documentation around that bug / in design documents
482 // helps provide more context about this.)
483 void RemoteWorkerChild::InitializeOnWorker() {
484 nsCOMPtr<nsIRunnable> r =
485 NewRunnableMethod("TransitionStateToRunning", this,
486 &RemoteWorkerChild::TransitionStateToRunning);
487 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
490 RefPtr<GenericNonExclusivePromise> RemoteWorkerChild::GetTerminationPromise() {
491 auto launcherData = mLauncherData.Access();
492 return launcherData->mTerminationPromise.Ensure(__func__);
495 void RemoteWorkerChild::CreationSucceededOnAnyThread() {
496 CreationSucceededOrFailedOnAnyThread(true);
499 void RemoteWorkerChild::CreationFailedOnAnyThread() {
500 CreationSucceededOrFailedOnAnyThread(false);
503 void RemoteWorkerChild::CreationSucceededOrFailedOnAnyThread(
504 bool aDidCreationSucceed) {
505 #ifdef DEBUG
507 auto lock = mState.Lock();
508 MOZ_ASSERT_IF(aDidCreationSucceed, lock->is<Running>());
509 MOZ_ASSERT_IF(!aDidCreationSucceed, lock->is<Killed>());
511 #endif
513 RefPtr<RemoteWorkerChild> self = this;
515 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
516 __func__,
517 [self = std::move(self), didCreationSucceed = aDidCreationSucceed] {
518 auto launcherData = self->mLauncherData.Access();
520 if (!self->CanSend() || launcherData->mDidSendCreated) {
521 return;
524 Unused << self->SendCreated(didCreationSucceed);
525 launcherData->mDidSendCreated = true;
528 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
531 void RemoteWorkerChild::CloseWorkerOnMainThread() {
532 AssertIsOnMainThread();
534 LOG(("CloseWorkerOnMainThread[this=%p]", this));
536 // We can't hold the state lock while calling WorkerPrivate::Cancel because
537 // the lambda callback will want to touch the state, so save off the
538 // WorkerPrivate so we can cancel it (if we need to cancel it).
539 RefPtr<WorkerPrivate> cancelWith;
541 auto lock = mState.Lock();
543 if (lock->is<Pending>()) {
544 cancelWith = lock->as<Pending>().mWorkerPrivate;
545 // There should be no way for this code to run before we
546 // ExecWorkerOnMainThread runs, which means that either it should have
547 // set a WorkerPrivate on Pending, or its error handling should already
548 // have transitioned us to Canceled and Killing in that order. (It's
549 // also possible that it assigned a WorkerPrivate and subsequently we
550 // transitioned to Running, which would put us in the next branch.)
551 MOZ_DIAGNOSTIC_ASSERT(cancelWith);
552 } else if (lock->is<Running>()) {
553 cancelWith = lock->as<Running>().mWorkerPrivate;
557 // It's very okay for us to not have a WorkerPrivate here if we've already
558 // canceled the worker or if errors happened.
559 if (cancelWith) {
560 cancelWith->Cancel();
565 * Error reporting method
567 void RemoteWorkerChild::ErrorPropagation(const ErrorValue& aValue) {
568 MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread());
570 if (!CanSend()) {
571 return;
574 Unused << SendError(aValue);
577 void RemoteWorkerChild::ErrorPropagationDispatch(nsresult aError) {
578 MOZ_ASSERT(NS_FAILED(aError));
580 RefPtr<RemoteWorkerChild> self = this;
581 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
582 "RemoteWorkerChild::ErrorPropagationDispatch",
583 [self = std::move(self), aError]() { self->ErrorPropagation(aError); });
585 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
588 void RemoteWorkerChild::ErrorPropagationOnMainThread(
589 const WorkerErrorReport* aReport, bool aIsErrorEvent) {
590 AssertIsOnMainThread();
592 ErrorValue value;
593 if (aIsErrorEvent) {
594 ErrorData data(
595 aReport->mIsWarning, aReport->mLineNumber, aReport->mColumnNumber,
596 aReport->mMessage, aReport->mFilename, aReport->mLine,
597 TransformIntoNewArray(aReport->mNotes, [](const WorkerErrorNote& note) {
598 return ErrorDataNote(note.mLineNumber, note.mColumnNumber,
599 note.mMessage, note.mFilename);
600 }));
601 value = data;
602 } else {
603 value = void_t();
606 RefPtr<RemoteWorkerChild> self = this;
607 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
608 "RemoteWorkerChild::ErrorPropagationOnMainThread",
609 [self = std::move(self), value]() { self->ErrorPropagation(value); });
611 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
614 void RemoteWorkerChild::CSPViolationPropagationOnMainThread(
615 const nsAString& aJSON) {
616 AssertIsOnMainThread();
618 RefPtr<RemoteWorkerChild> self = this;
619 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
620 "RemoteWorkerChild::ErrorPropagationDispatch",
621 [self = std::move(self), json = nsString(aJSON)]() {
622 CSPViolation violation(json);
623 self->ErrorPropagation(violation);
626 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
629 void RemoteWorkerChild::NotifyLock(bool aCreated) {
630 nsCOMPtr<nsIRunnable> r =
631 NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] {
632 if (!self->CanSend()) {
633 return;
636 Unused << self->SendNotifyLock(aCreated);
639 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
642 void RemoteWorkerChild::NotifyWebTransport(bool aCreated) {
643 nsCOMPtr<nsIRunnable> r =
644 NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] {
645 if (!self->CanSend()) {
646 return;
649 Unused << self->SendNotifyWebTransport(aCreated);
652 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
655 void RemoteWorkerChild::FlushReportsOnMainThread(
656 nsIConsoleReportCollector* aReporter) {
657 AssertIsOnMainThread();
659 bool reportErrorToBrowserConsole = true;
661 // Flush the reports.
662 for (uint32_t i = 0, len = mWindowIDs.Length(); i < len; ++i) {
663 aReporter->FlushReportsToConsole(
664 mWindowIDs[i], nsIConsoleReportCollector::ReportAction::Save);
665 reportErrorToBrowserConsole = false;
668 // Finally report to browser console if there is no any window.
669 if (reportErrorToBrowserConsole) {
670 aReporter->FlushReportsToConsole(0);
671 return;
674 aReporter->ClearConsoleReports();
678 * Worker state transition methods
680 RemoteWorkerChild::WorkerPrivateAccessibleState::
681 ~WorkerPrivateAccessibleState() {
682 // We should now only be performing state transitions on the main thread, so
683 // we should assert we're only releasing on the main thread.
684 MOZ_ASSERT(!mWorkerPrivate || NS_IsMainThread());
685 // mWorkerPrivate can be safely released on the main thread.
686 if (!mWorkerPrivate || NS_IsMainThread()) {
687 return;
690 // But as a backstop, do proxy the release to the main thread.
691 NS_ReleaseOnMainThread(
692 "RemoteWorkerChild::WorkerPrivateAccessibleState::mWorkerPrivate",
693 mWorkerPrivate.forget());
696 void RemoteWorkerChild::
697 OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled() {
698 auto lock = mState.Lock();
700 LOG(("TransitionStateFromPendingOrRunningToCanceled[this=%p]", this));
702 if (lock->is<Pending>()) {
703 TransitionStateFromPendingToCanceled(lock.ref());
704 } else if (lock->is<Running>()) {
705 *lock = VariantType<Canceled>();
706 } else {
707 MOZ_ASSERT(false, "State should have been Pending or Running");
711 void RemoteWorkerChild::TransitionStateFromPendingToCanceled(State& aState) {
712 AssertIsOnMainThread();
713 MOZ_ASSERT(aState.is<Pending>());
714 LOG(("TransitionStateFromPendingToCanceled[this=%p]", this));
716 CancelAllPendingOps(aState);
718 aState = VariantType<Canceled>();
721 void RemoteWorkerChild::TransitionStateFromCanceledToKilled() {
722 AssertIsOnMainThread();
724 LOG(("TransitionStateFromCanceledToKilled[this=%p]", this));
726 auto lock = mState.Lock();
727 MOZ_ASSERT(lock->is<Canceled>());
729 *lock = VariantType<Killed>();
731 RefPtr<RemoteWorkerChild> self = this;
732 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() {
733 auto launcherData = self->mLauncherData.Access();
735 // (We maintain the historical ordering of resolving this promise prior to
736 // calling SendClose, however the previous code used 2 separate dispatches
737 // to this thread for the resolve and SendClose, and there inherently
738 // would be a race between the runnables resulting from the resolved
739 // promise and the promise containing the call to SendClose. Now it's
740 // entirely clear that our call to SendClose will effectively run before
741 // any of the resolved promises are able to do anything.)
742 launcherData->mTerminationPromise.ResolveIfExists(true, __func__);
744 if (self->CanSend()) {
745 Unused << self->SendClose();
749 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
752 void RemoteWorkerChild::TransitionStateToRunning() {
753 AssertIsOnMainThread();
755 LOG(("TransitionStateToRunning[this=%p]", this));
757 nsTArray<RefPtr<Op>> pendingOps;
760 auto lock = mState.Lock();
762 // Because this is an async notification sent from the worker to the main
763 // thread, it's very possible that we've already decided on the main thread
764 // to transition to the Canceled state, in which case there is nothing for
765 // us to do here.
766 if (!lock->is<Pending>()) {
767 LOG(("State is already not pending in TransitionStateToRunning[this=%p]!",
768 this));
769 return;
772 RefPtr<WorkerPrivate> workerPrivate =
773 std::move(lock->as<Pending>().mWorkerPrivate);
774 pendingOps = std::move(lock->as<Pending>().mPendingOps);
776 // Move the worker private into place to avoid gratuitous ref churn; prior
777 // comments here suggest the Variant can't accept a move.
778 *lock = VariantType<Running>();
779 lock->as<Running>().mWorkerPrivate = std::move(workerPrivate);
782 CreationSucceededOnAnyThread();
784 RefPtr<RemoteWorkerChild> self = this;
785 for (auto& op : pendingOps) {
786 op->StartOnMainThread(self);
790 void RemoteWorkerChild::ExceptionalErrorTransitionDuringExecWorker() {
791 AssertIsOnMainThread();
793 LOG(("ExceptionalErrorTransitionDuringExecWorker[this=%p]", this));
795 // This method is called synchronously by ExecWorkerOnMainThread in the event
796 // of any error. Because we only transition to Running on the main thread
797 // as the result of a notification from the worker, we know our state will be
798 // Pending, but mWorkerPrivate may or may not be null, as we may not have
799 // gotten to spawning the worker.
801 // In the event the worker exists, we need to Cancel() it. We must do this
802 // without the lock held because our call to Cancel() will invoke the
803 // cancellation callback we created which will call TransitionStateToCanceled,
804 // and we can't be holding the lock when that happens.
806 RefPtr<WorkerPrivate> cancelWith;
809 auto lock = mState.Lock();
811 MOZ_ASSERT(lock->is<Pending>());
812 if (lock->is<Pending>()) {
813 cancelWith = lock->as<Pending>().mWorkerPrivate;
814 if (!cancelWith) {
815 // The worker wasn't actually created, so we should synthetically
816 // transition to canceled and onward. Since we have the lock,
817 // perform the transition now for clarity, but we'll handle the rest of
818 // this case after dropping the lock.
819 TransitionStateFromPendingToCanceled(lock.ref());
824 if (cancelWith) {
825 cancelWith->Cancel();
826 } else {
827 TransitionStateFromCanceledToKilled();
828 CreationFailedOnAnyThread();
833 * Operation execution classes/methods
835 class RemoteWorkerChild::SharedWorkerOp : public RemoteWorkerChild::Op {
836 public:
837 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerOp, override)
839 explicit SharedWorkerOp(RemoteWorkerOp&& aOp) : mOp(std::move(aOp)) {}
841 bool MaybeStart(RemoteWorkerChild* aOwner,
842 RemoteWorkerChild::State& aState) override {
843 MOZ_ASSERT(!mStarted);
844 MOZ_ASSERT(aOwner);
845 // Thread: We are on the Worker Launcher thread.
847 // Return false, indicating we should queue this op if our current state is
848 // pending and this isn't a termination op (which should skip the line).
849 if (aState.is<Pending>() && !IsTerminationOp()) {
850 return false;
853 // If the worker is already shutting down (which should be unexpected
854 // because we should be told new operations after a termination op), just
855 // return true to indicate the op should be discarded.
856 if (aState.is<Canceled>() || aState.is<Killed>()) {
857 #ifdef DEBUG
858 mStarted = true;
859 #endif
860 if (mOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
861 MessagePort::ForceClose(
862 mOp.get_RemoteWorkerPortIdentifierOp().portIdentifier());
864 return true;
867 MOZ_ASSERT(aState.is<Running>() || IsTerminationOp());
869 RefPtr<SharedWorkerOp> self = this;
870 RefPtr<RemoteWorkerChild> owner = aOwner;
872 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
873 __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
875 auto lock = owner->mState.Lock();
877 if (NS_WARN_IF(lock->is<Canceled>() || lock->is<Killed>())) {
878 self->Cancel();
879 // Worker has already canceled, force close the MessagePort.
880 if (self->mOp.type() ==
881 RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
882 MessagePort::ForceClose(
883 self->mOp.get_RemoteWorkerPortIdentifierOp()
884 .portIdentifier());
886 return;
890 self->StartOnMainThread(owner);
893 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
895 #ifdef DEBUG
896 mStarted = true;
897 #endif
899 return true;
902 void StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) final {
903 using Running = RemoteWorkerChild::Running;
905 AssertIsOnMainThread();
907 if (IsTerminationOp()) {
908 aOwner->CloseWorkerOnMainThread();
909 return;
912 auto lock = aOwner->mState.Lock();
913 MOZ_ASSERT(lock->is<Running>());
914 if (!lock->is<Running>()) {
915 aOwner->ErrorPropagationDispatch(NS_ERROR_DOM_INVALID_STATE_ERR);
916 return;
919 RefPtr<WorkerPrivate> workerPrivate = lock->as<Running>().mWorkerPrivate;
921 MOZ_ASSERT(workerPrivate);
923 if (mOp.type() == RemoteWorkerOp::TRemoteWorkerSuspendOp) {
924 workerPrivate->ParentWindowPaused();
925 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerResumeOp) {
926 workerPrivate->ParentWindowResumed();
927 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerFreezeOp) {
928 workerPrivate->Freeze(nullptr);
929 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerThawOp) {
930 workerPrivate->Thaw(nullptr);
931 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
932 RefPtr<MessagePortIdentifierRunnable> r =
933 new MessagePortIdentifierRunnable(
934 workerPrivate, aOwner,
935 mOp.get_RemoteWorkerPortIdentifierOp().portIdentifier());
936 if (NS_WARN_IF(!r->Dispatch())) {
937 aOwner->ErrorPropagationDispatch(NS_ERROR_FAILURE);
939 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerAddWindowIDOp) {
940 aOwner->mWindowIDs.AppendElement(
941 mOp.get_RemoteWorkerAddWindowIDOp().windowID());
942 } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerRemoveWindowIDOp) {
943 aOwner->mWindowIDs.RemoveElement(
944 mOp.get_RemoteWorkerRemoveWindowIDOp().windowID());
945 } else {
946 MOZ_CRASH("Unknown RemoteWorkerOp type!");
950 void Cancel() override {
951 #ifdef DEBUG
952 mStarted = true;
953 #endif
956 private:
957 ~SharedWorkerOp() { MOZ_ASSERT(mStarted); }
959 bool IsTerminationOp() const {
960 return mOp.type() == RemoteWorkerOp::TRemoteWorkerTerminateOp;
963 RemoteWorkerOp mOp;
965 #ifdef DEBUG
966 bool mStarted = false;
967 #endif
970 void RemoteWorkerChild::AddPortIdentifier(
971 JSContext* aCx, WorkerPrivate* aWorkerPrivate,
972 UniqueMessagePortId& aPortIdentifier) {
973 if (NS_WARN_IF(!aWorkerPrivate->ConnectMessagePort(aCx, aPortIdentifier))) {
974 ErrorPropagationDispatch(NS_ERROR_FAILURE);
978 void RemoteWorkerChild::CancelAllPendingOps(State& aState) {
979 MOZ_ASSERT(aState.is<Pending>());
981 auto pendingOps = std::move(aState.as<Pending>().mPendingOps);
983 for (auto& op : pendingOps) {
984 op->Cancel();
988 void RemoteWorkerChild::MaybeStartOp(RefPtr<Op>&& aOp) {
989 MOZ_ASSERT(aOp);
991 auto lock = mState.Lock();
993 if (!aOp->MaybeStart(this, lock.ref())) {
994 // Maybestart returns false only if we are <Pending>.
995 lock->as<Pending>().mPendingOps.AppendElement(std::move(aOp));
999 IPCResult RemoteWorkerChild::RecvExecOp(RemoteWorkerOp&& aOp) {
1000 MOZ_ASSERT(!mIsServiceWorker);
1002 MaybeStartOp(new SharedWorkerOp(std::move(aOp)));
1004 return IPC_OK();
1007 IPCResult RemoteWorkerChild::RecvExecServiceWorkerOp(
1008 ServiceWorkerOpArgs&& aArgs, ExecServiceWorkerOpResolver&& aResolve) {
1009 MOZ_ASSERT(mIsServiceWorker);
1010 MOZ_ASSERT(
1011 aArgs.type() !=
1012 ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs,
1013 "FetchEvent operations should be sent via PFetchEventOp(Proxy) actors!");
1015 MaybeReportServiceWorkerShutdownProgress(aArgs);
1017 MaybeStartOp(ServiceWorkerOp::Create(std::move(aArgs), std::move(aResolve)));
1019 return IPC_OK();
1022 RefPtr<GenericPromise>
1023 RemoteWorkerChild::MaybeSendSetServiceWorkerSkipWaitingFlag() {
1024 RefPtr<GenericPromise::Private> promise =
1025 new GenericPromise::Private(__func__);
1027 RefPtr<RemoteWorkerChild> self = this;
1029 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self = std::move(
1030 self),
1031 promise] {
1032 if (!self->CanSend()) {
1033 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
1034 return;
1037 self->SendSetServiceWorkerSkipWaitingFlag()->Then(
1038 GetCurrentSerialEventTarget(), __func__,
1039 [promise](
1040 const SetServiceWorkerSkipWaitingFlagPromise::ResolveOrRejectValue&
1041 aResult) {
1042 if (NS_WARN_IF(aResult.IsReject())) {
1043 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
1044 return;
1047 promise->Resolve(aResult.ResolveValue(), __func__);
1051 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
1053 return promise;
1057 * PFetchEventOpProxy methods
1059 already_AddRefed<PFetchEventOpProxyChild>
1060 RemoteWorkerChild::AllocPFetchEventOpProxyChild(
1061 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
1062 return RefPtr{new FetchEventOpProxyChild()}.forget();
1065 IPCResult RemoteWorkerChild::RecvPFetchEventOpProxyConstructor(
1066 PFetchEventOpProxyChild* aActor,
1067 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
1068 MOZ_ASSERT(aActor);
1070 (static_cast<FetchEventOpProxyChild*>(aActor))->Initialize(aArgs);
1072 return IPC_OK();
1075 } // namespace dom
1076 } // namespace mozilla