RemoteWorkerChild.cpp (30874B)
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 "RemoteWorkerService.h" 13 #include "mozilla/ArrayAlgorithm.h" 14 #include "mozilla/Assertions.h" 15 #include "mozilla/BasePrincipal.h" 16 #include "mozilla/ErrorResult.h" 17 #include "mozilla/PermissionManager.h" 18 #include "mozilla/SchedulerGroup.h" 19 #include "mozilla/ScopeExit.h" 20 #include "mozilla/Services.h" 21 #include "mozilla/dom/FetchEventOpProxyChild.h" 22 #include "mozilla/dom/IndexedDatabaseManager.h" 23 #include "mozilla/dom/MessagePort.h" 24 #include "mozilla/dom/PolicyContainer.h" 25 #include "mozilla/dom/RemoteWorkerTypes.h" 26 #include "mozilla/dom/ServiceWorkerDescriptor.h" 27 #include "mozilla/dom/ServiceWorkerInterceptController.h" 28 #include "mozilla/dom/ServiceWorkerOp.h" 29 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" 30 #include "mozilla/dom/ServiceWorkerShutdownState.h" 31 #include "mozilla/dom/ServiceWorkerUtils.h" 32 #include "mozilla/dom/SharedWorkerOp.h" 33 #include "mozilla/dom/WorkerCSPContext.h" 34 #include "mozilla/dom/WorkerError.h" 35 #include "mozilla/dom/WorkerPrivate.h" 36 #include "mozilla/dom/WorkerRef.h" 37 #include "mozilla/dom/WorkerRunnable.h" 38 #include "mozilla/dom/WorkerScope.h" 39 #include "mozilla/dom/workerinternals/ScriptLoader.h" 40 #include "mozilla/ipc/BackgroundUtils.h" 41 #include "mozilla/ipc/URIUtils.h" 42 #include "mozilla/net/CookieJarSettings.h" 43 #include "nsCOMPtr.h" 44 #include "nsDebug.h" 45 #include "nsError.h" 46 #include "nsIConsoleReportCollector.h" 47 #include "nsIInterfaceRequestor.h" 48 #include "nsIPrincipal.h" 49 #include "nsNetUtil.h" 50 #include "nsThreadUtils.h" 51 #include "nsXULAppAPI.h" 52 53 mozilla::LazyLogModule gRemoteWorkerChildLog("RemoteWorkerChild"); 54 55 #ifdef LOG 56 # undef LOG 57 #endif 58 #define LOG(fmt) MOZ_LOG(gRemoteWorkerChildLog, mozilla::LogLevel::Verbose, fmt) 59 60 namespace mozilla { 61 62 using namespace ipc; 63 64 namespace dom { 65 66 using workerinternals::ChannelFromScriptURLMainThread; 67 68 using remoteworker::Canceled; 69 using remoteworker::Killed; 70 using remoteworker::Pending; 71 using remoteworker::Running; 72 73 namespace { 74 75 class SharedWorkerInterfaceRequestor final : public nsIInterfaceRequestor { 76 public: 77 NS_DECL_ISUPPORTS 78 79 SharedWorkerInterfaceRequestor() { 80 // This check must match the code nsDocShell::Create. 81 if (XRE_IsParentProcess()) { 82 mSWController = new ServiceWorkerInterceptController(); 83 } 84 } 85 86 NS_IMETHOD 87 GetInterface(const nsIID& aIID, void** aSink) override { 88 MOZ_ASSERT(NS_IsMainThread()); 89 90 if (mSWController && 91 aIID.Equals(NS_GET_IID(nsINetworkInterceptController))) { 92 // If asked for the network intercept controller, ask the outer requestor, 93 // which could be the docshell. 94 RefPtr<ServiceWorkerInterceptController> swController = mSWController; 95 swController.forget(aSink); 96 return NS_OK; 97 } 98 99 return NS_NOINTERFACE; 100 } 101 102 private: 103 ~SharedWorkerInterfaceRequestor() = default; 104 105 RefPtr<ServiceWorkerInterceptController> mSWController; 106 }; 107 108 NS_IMPL_ADDREF(SharedWorkerInterfaceRequestor) 109 NS_IMPL_RELEASE(SharedWorkerInterfaceRequestor) 110 NS_IMPL_QUERY_INTERFACE(SharedWorkerInterfaceRequestor, nsIInterfaceRequestor) 111 112 // This is used to propagate the CSP violation when loading the SharedWorker 113 // main-script and nothing else. 114 class RemoteWorkerCSPEventListener final : public nsICSPEventListener { 115 public: 116 NS_DECL_ISUPPORTS 117 118 explicit RemoteWorkerCSPEventListener(RemoteWorkerChild* aActor) 119 : mActor(aActor) {}; 120 121 NS_IMETHOD OnCSPViolationEvent(const nsAString& aJSON) override { 122 mActor->CSPViolationPropagationOnMainThread(aJSON); 123 return NS_OK; 124 } 125 126 private: 127 ~RemoteWorkerCSPEventListener() = default; 128 129 RefPtr<RemoteWorkerChild> mActor; 130 }; 131 132 NS_IMPL_ISUPPORTS(RemoteWorkerCSPEventListener, nsICSPEventListener) 133 134 } // anonymous namespace 135 136 RemoteWorkerChild::RemoteWorkerChild(const RemoteWorkerData& aData) 137 : mState(VariantType<remoteworker::Pending>(), "RemoteWorkerState"), 138 mServiceKeepAlive(RemoteWorkerService::MaybeGetKeepAlive()), 139 mIsServiceWorker(aData.serviceWorkerData().type() == 140 OptionalServiceWorkerData::TServiceWorkerData), 141 mPendingOps("PendingRemoteWorkerOps") { 142 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread()); 143 } 144 145 RemoteWorkerChild::~RemoteWorkerChild() { 146 #ifdef DEBUG 147 auto lock = mState.Lock(); 148 MOZ_ASSERT(lock->is<Killed>()); 149 #endif 150 } 151 152 void RemoteWorkerChild::ActorDestroy(ActorDestroyReason) { 153 auto launcherData = mLauncherData.Access(); 154 155 (void)NS_WARN_IF(!launcherData->mTerminationPromise.IsEmpty()); 156 launcherData->mTerminationPromise.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, 157 __func__); 158 159 auto lock = mState.Lock(); 160 161 // If the worker hasn't shutdown or begun shutdown, we need to ensure it gets 162 // canceled. 163 if (NS_WARN_IF(!lock->is<Killed>() && !lock->is<Canceled>())) { 164 // In terms of strong references to this RemoteWorkerChild, at this moment: 165 // - IPC is holding a strong reference that will be dropped in the near 166 // future after this method returns. 167 // - If the worker has been started by ExecWorkerOnMainThread, then 168 // WorkerPrivate::mRemoteWorkerController is a strong reference to us. 169 // If the worker has not been started, ExecWorker's runnable lambda will 170 // have a strong reference that will cover the call to 171 // ExecWorkerOnMainThread. 172 // - The WorkerPrivate cancellation and termination callbacks will also 173 // hold strong references, but those callbacks will not outlive the 174 // WorkerPrivate and are not exposed to callers like 175 // mRemoteWorkerController is. 176 // 177 // Note that this call to RequestWorkerCancellation can still race worker 178 // cancellation, in which case the strong reference obtained by 179 // NewRunnableMethod can end up being the last strong reference. 180 // (RequestWorkerCancellation handles the case that the Worker is already 181 // canceled if this happens.) 182 RefPtr<nsIRunnable> runnable = 183 NewRunnableMethod("RequestWorkerCancellation", this, 184 &RemoteWorkerChild::RequestWorkerCancellation); 185 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(runnable.forget())); 186 } 187 } 188 189 void RemoteWorkerChild::ExecWorker( 190 const RemoteWorkerData& aData, 191 mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&& 192 aChildEp) { 193 #ifdef DEBUG 194 MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread()); 195 auto launcherData = mLauncherData.Access(); 196 MOZ_ASSERT(CanSend()); 197 #endif 198 199 RefPtr<RemoteWorkerChild> self = this; 200 201 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 202 __func__, [self = std::move(self), data = aData, 203 childEp = std::move(aChildEp)]() mutable { 204 nsresult rv = 205 self->ExecWorkerOnMainThread(std::move(data), std::move(childEp)); 206 207 // Creation failure will already have been reported via the method 208 // above internally using ScopeExit. 209 (void)NS_WARN_IF(NS_FAILED(rv)); 210 }); 211 212 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 213 } 214 215 nsresult RemoteWorkerChild::ExecWorkerOnMainThread( 216 RemoteWorkerData&& aData, 217 mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&& 218 aChildEp) { 219 MOZ_ASSERT(NS_IsMainThread()); 220 221 // Ensure that the IndexedDatabaseManager is initialized so that if any 222 // workers do any IndexedDB calls that all of IDB's prefs/etc. are 223 // initialized. 224 IndexedDatabaseManager* idm = IndexedDatabaseManager::GetOrCreate(); 225 if (idm) { 226 (void)NS_WARN_IF(NS_FAILED(idm->EnsureLocale())); 227 } else { 228 NS_WARNING("Failed to get IndexedDatabaseManager!"); 229 } 230 231 auto scopeExit = 232 MakeScopeExit([&] { ExceptionalErrorTransitionDuringExecWorker(); }); 233 234 auto principalOrErr = PrincipalInfoToPrincipal(aData.principalInfo()); 235 if (NS_WARN_IF(principalOrErr.isErr())) { 236 return principalOrErr.unwrapErr(); 237 } 238 239 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 240 241 auto loadingPrincipalOrErr = 242 PrincipalInfoToPrincipal(aData.loadingPrincipalInfo()); 243 if (NS_WARN_IF(loadingPrincipalOrErr.isErr())) { 244 return loadingPrincipalOrErr.unwrapErr(); 245 } 246 247 auto partitionedPrincipalOrErr = 248 PrincipalInfoToPrincipal(aData.partitionedPrincipalInfo()); 249 if (NS_WARN_IF(partitionedPrincipalOrErr.isErr())) { 250 return partitionedPrincipalOrErr.unwrapErr(); 251 } 252 253 WorkerLoadInfo info; 254 info.mBaseURI = DeserializeURI(aData.baseScriptURL()); 255 info.mResolvedScriptURI = DeserializeURI(aData.resolvedScriptURL()); 256 257 info.mPrincipalInfo = MakeUnique<PrincipalInfo>(aData.principalInfo()); 258 info.mPartitionedPrincipalInfo = 259 MakeUnique<PrincipalInfo>(aData.partitionedPrincipalInfo()); 260 261 info.mReferrerInfo = aData.referrerInfo(); 262 info.mDomain = aData.domain(); 263 info.mTrials = aData.originTrials(); 264 info.mPrincipal = principal; 265 info.mPartitionedPrincipal = partitionedPrincipalOrErr.unwrap(); 266 info.mLoadingPrincipal = loadingPrincipalOrErr.unwrap(); 267 info.mStorageAccess = aData.storageAccess(); 268 info.mUseRegularPrincipal = aData.useRegularPrincipal(); 269 info.mUsingStorageAccess = aData.usingStorageAccess(); 270 info.mIsThirdPartyContext = aData.isThirdPartyContext(); 271 info.mOriginAttributes = 272 BasePrincipal::Cast(principal)->OriginAttributesRef(); 273 info.mShouldResistFingerprinting = aData.shouldResistFingerprinting(); 274 Maybe<RFPTargetSet> overriddenFingerprintingSettings; 275 if (aData.overriddenFingerprintingSettings().isSome()) { 276 overriddenFingerprintingSettings.emplace( 277 aData.overriddenFingerprintingSettings().ref()); 278 } 279 info.mOverriddenFingerprintingSettings = overriddenFingerprintingSettings; 280 net::CookieJarSettings::Deserialize(aData.cookieJarSettings(), 281 getter_AddRefs(info.mCookieJarSettings)); 282 info.mCookieJarSettingsArgs = aData.cookieJarSettings(); 283 info.mIsOn3PCBExceptionList = aData.isOn3PCBExceptionList(); 284 info.mSecureContext = aData.isSecureContext() 285 ? WorkerLoadInfo::eSecureContext 286 : WorkerLoadInfo::eInsecureContext; 287 288 WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mLoadingPrincipal); 289 290 RefPtr<SharedWorkerInterfaceRequestor> requestor = 291 new SharedWorkerInterfaceRequestor(); 292 info.mInterfaceRequestor->SetOuterRequestor(requestor); 293 294 Maybe<ClientInfo> clientInfo; 295 if (aData.clientInfo().isSome()) { 296 clientInfo.emplace(ClientInfo(aData.clientInfo().ref())); 297 } 298 299 if (mIsServiceWorker) { 300 info.mSourceInfo = clientInfo; 301 } else { 302 if (clientInfo.isSome()) { 303 Maybe<mozilla::ipc::PolicyContainerArgs> policyContainerArgs = 304 clientInfo.ref().GetPolicyContainerArgs(); 305 if (policyContainerArgs.isSome() && policyContainerArgs->csp().isSome()) { 306 info.mCSP = CSPInfoToCSP(*policyContainerArgs->csp(), nullptr); 307 mozilla::Result<UniquePtr<WorkerCSPContext>, nsresult> ctx = 308 WorkerCSPContext::CreateFromCSP(info.mCSP); 309 if (ctx.isErr()) { 310 return ctx.unwrapErr(); 311 } 312 info.mCSPContext = ctx.unwrap(); 313 } 314 } 315 } 316 317 nsresult rv = info.SetPrincipalsAndCSPOnMainThread( 318 info.mPrincipal, info.mPartitionedPrincipal, info.mLoadGroup, info.mCSP); 319 if (NS_WARN_IF(NS_FAILED(rv))) { 320 return rv; 321 } 322 323 nsString workerPrivateId; 324 325 if (mIsServiceWorker) { 326 ServiceWorkerData& data = aData.serviceWorkerData().get_ServiceWorkerData(); 327 328 MOZ_ASSERT(!data.id().IsEmpty()); 329 workerPrivateId = std::move(data.id()); 330 331 info.mServiceWorkerCacheName = data.cacheName(); 332 info.mServiceWorkerDescriptor.emplace(data.descriptor()); 333 info.mServiceWorkerRegistrationDescriptor.emplace( 334 data.registrationDescriptor()); 335 info.mLoadFlags = static_cast<nsLoadFlags>(data.loadFlags()); 336 } else { 337 // Top level workers' main script use the document charset for the script 338 // uri encoding. 339 rv = ChannelFromScriptURLMainThread( 340 info.mLoadingPrincipal, nullptr /* parent document */, info.mLoadGroup, 341 info.mResolvedScriptURI, aData.workerOptions().mType, 342 aData.workerOptions().mCredentials, clientInfo, 343 nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER, info.mCookieJarSettings, 344 info.mReferrerInfo, getter_AddRefs(info.mChannel)); 345 if (NS_WARN_IF(NS_FAILED(rv))) { 346 return rv; 347 } 348 349 nsCOMPtr<nsILoadInfo> loadInfo = info.mChannel->LoadInfo(); 350 351 auto* cspEventListener = new RemoteWorkerCSPEventListener(this); 352 rv = loadInfo->SetCspEventListener(cspEventListener); 353 if (NS_WARN_IF(NS_FAILED(rv))) { 354 return rv; 355 } 356 } 357 358 info.mAgentClusterId = aData.agentClusterId(); 359 360 AutoJSAPI jsapi; 361 jsapi.Init(); 362 363 ErrorResult error; 364 RefPtr<RemoteWorkerChild> self = this; 365 RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor( 366 jsapi.cx(), aData.originalScriptURL(), false, 367 mIsServiceWorker ? WorkerKindService : WorkerKindShared, 368 aData.workerOptions().mCredentials, aData.workerOptions().mType, 369 aData.workerOptions().mName, VoidCString(), &info, error, 370 std::move(workerPrivateId), 371 [self](bool aEverRan) { 372 self->OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled(); 373 }, 374 // This will be invoked here on the main thread when the worker is already 375 // fully shutdown. This replaces a prior approach where we would 376 // begin to transition when the worker thread would reach the Canceling 377 // state. This lambda ensures that we not only wait for the Killing state 378 // to be reached but that the global shutdown has already occurred. 379 [self]() { self->TransitionStateFromCanceledToKilled(); }, 380 std::move(aChildEp)); 381 382 if (NS_WARN_IF(error.Failed())) { 383 MOZ_ASSERT(!workerPrivate); 384 385 rv = error.StealNSResult(); 386 return rv; 387 } 388 389 workerPrivate->SetRemoteWorkerController(this); 390 391 // This wants to run as a normal task sequentially after the top level script 392 // evaluation, so the hybrid target is the correct choice between hybrid and 393 // `ControlEventTarget`. 394 nsCOMPtr<nsISerialEventTarget> workerTarget = 395 workerPrivate->HybridEventTarget(); 396 397 nsCOMPtr<nsIRunnable> runnable = NewCancelableRunnableMethod( 398 "InitialzeOnWorker", this, &RemoteWorkerChild::InitializeOnWorker); 399 400 { 401 MOZ_ASSERT(workerPrivate); 402 auto lock = mState.Lock(); 403 // We MUST be pending here, so direct access is ok. 404 lock->as<Pending>().mWorkerPrivate = std::move(workerPrivate); 405 } 406 407 if (mIsServiceWorker) { 408 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 409 __func__, [workerTarget, 410 initializeWorkerRunnable = std::move(runnable)]() mutable { 411 (void)NS_WARN_IF(NS_FAILED( 412 workerTarget->Dispatch(initializeWorkerRunnable.forget()))); 413 }); 414 415 RefPtr<PermissionManager> permissionManager = 416 PermissionManager::GetInstance(); 417 if (!permissionManager) { 418 return NS_ERROR_FAILURE; 419 } 420 permissionManager->WhenPermissionsAvailable(principal, r); 421 } else { 422 if (NS_WARN_IF(NS_FAILED(workerTarget->Dispatch(runnable.forget())))) { 423 rv = NS_ERROR_FAILURE; 424 return rv; 425 } 426 } 427 428 scopeExit.release(); 429 430 return NS_OK; 431 } 432 433 void RemoteWorkerChild::RequestWorkerCancellation() { 434 MOZ_ASSERT(NS_IsMainThread()); 435 436 LOG(("RequestWorkerCancellation[this=%p]", this)); 437 438 // We want to ensure that we've requested the worker be canceled. So if the 439 // worker is running, cancel it. We can't do this with the lock held, 440 // however, because our lambdas will want to manipulate the state. 441 RefPtr<WorkerPrivate> cancelWith; 442 { 443 auto lock = mState.Lock(); 444 if (lock->is<Pending>()) { 445 cancelWith = lock->as<Pending>().mWorkerPrivate; 446 } else if (lock->is<Running>()) { 447 cancelWith = lock->as<Running>().mWorkerPrivate; 448 } 449 } 450 451 if (cancelWith) { 452 cancelWith->Cancel(); 453 } 454 } 455 456 // This method will be invoked on the worker after the top-level 457 // CompileScriptRunnable task has succeeded and as long as the worker has not 458 // been closed/canceled. There are edge-cases related to cancellation, but we 459 // have our caller ensure that we are only called as long as the worker's state 460 // is Running. 461 // 462 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1800659 will eliminate 463 // cancellation, and the documentation around that bug / in design documents 464 // helps provide more context about this.) 465 void RemoteWorkerChild::InitializeOnWorker() { 466 nsCOMPtr<nsIRunnable> r = 467 NewRunnableMethod("TransitionStateToRunning", this, 468 &RemoteWorkerChild::TransitionStateToRunning); 469 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 470 } 471 472 RefPtr<GenericNonExclusivePromise> RemoteWorkerChild::GetTerminationPromise() { 473 auto launcherData = mLauncherData.Access(); 474 return launcherData->mTerminationPromise.Ensure(__func__); 475 } 476 477 void RemoteWorkerChild::CreationSucceededOnAnyThread() { 478 CreationSucceededOrFailedOnAnyThread(true); 479 } 480 481 void RemoteWorkerChild::CreationFailedOnAnyThread() { 482 CreationSucceededOrFailedOnAnyThread(false); 483 } 484 485 void RemoteWorkerChild::CreationSucceededOrFailedOnAnyThread( 486 bool aDidCreationSucceed) { 487 #ifdef DEBUG 488 { 489 auto lock = mState.Lock(); 490 MOZ_ASSERT_IF(aDidCreationSucceed, lock->is<Running>()); 491 MOZ_ASSERT_IF(!aDidCreationSucceed, lock->is<Killed>()); 492 } 493 #endif 494 495 RefPtr<RemoteWorkerChild> self = this; 496 497 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 498 __func__, 499 [self = std::move(self), didCreationSucceed = aDidCreationSucceed] { 500 auto launcherData = self->mLauncherData.Access(); 501 502 if (!self->CanSend() || launcherData->mDidSendCreated) { 503 return; 504 } 505 506 (void)self->SendCreated(didCreationSucceed); 507 launcherData->mDidSendCreated = true; 508 }); 509 510 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 511 } 512 513 void RemoteWorkerChild::CloseWorkerOnMainThread() { 514 AssertIsOnMainThread(); 515 516 LOG(("CloseWorkerOnMainThread[this=%p]", this)); 517 518 // We can't hold the state lock while calling WorkerPrivate::Cancel because 519 // the lambda callback will want to touch the state, so save off the 520 // WorkerPrivate so we can cancel it (if we need to cancel it). 521 RefPtr<WorkerPrivate> cancelWith; 522 { 523 auto lock = mState.Lock(); 524 525 if (lock->is<Pending>()) { 526 cancelWith = lock->as<Pending>().mWorkerPrivate; 527 // There should be no way for this code to run before we 528 // ExecWorkerOnMainThread runs, which means that either it should have 529 // set a WorkerPrivate on Pending, or its error handling should already 530 // have transitioned us to Canceled and Killing in that order. (It's 531 // also possible that it assigned a WorkerPrivate and subsequently we 532 // transitioned to Running, which would put us in the next branch.) 533 MOZ_DIAGNOSTIC_ASSERT(cancelWith); 534 } else if (lock->is<Running>()) { 535 cancelWith = lock->as<Running>().mWorkerPrivate; 536 } 537 } 538 539 // It's very okay for us to not have a WorkerPrivate here if we've already 540 // canceled the worker or if errors happened. 541 if (cancelWith) { 542 cancelWith->Cancel(); 543 } 544 } 545 546 /** 547 * Error reporting method 548 */ 549 void RemoteWorkerChild::ErrorPropagation(const ErrorValue& aValue) { 550 MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread()); 551 552 if (!CanSend()) { 553 return; 554 } 555 556 (void)SendError(aValue); 557 } 558 559 void RemoteWorkerChild::ErrorPropagationDispatch(nsresult aError) { 560 MOZ_ASSERT(NS_FAILED(aError)); 561 562 RefPtr<RemoteWorkerChild> self = this; 563 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 564 "RemoteWorkerChild::ErrorPropagationDispatch", 565 [self = std::move(self), aError]() { self->ErrorPropagation(aError); }); 566 567 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 568 } 569 570 void RemoteWorkerChild::ErrorPropagationOnMainThread( 571 const WorkerErrorReport* aReport, bool aIsErrorEvent) { 572 AssertIsOnMainThread(); 573 574 ErrorValue value; 575 if (aIsErrorEvent) { 576 ErrorData data( 577 aReport->mIsWarning, aReport->mLineNumber, aReport->mColumnNumber, 578 aReport->mMessage, aReport->mFilename, 579 TransformIntoNewArray(aReport->mNotes, [](const WorkerErrorNote& note) { 580 return ErrorDataNote(note.mLineNumber, note.mColumnNumber, 581 note.mMessage, note.mFilename); 582 })); 583 value = data; 584 } else { 585 value = void_t(); 586 } 587 588 RefPtr<RemoteWorkerChild> self = this; 589 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 590 "RemoteWorkerChild::ErrorPropagationOnMainThread", 591 [self = std::move(self), value]() { self->ErrorPropagation(value); }); 592 593 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 594 } 595 596 void RemoteWorkerChild::CSPViolationPropagationOnMainThread( 597 const nsAString& aJSON) { 598 AssertIsOnMainThread(); 599 600 RefPtr<RemoteWorkerChild> self = this; 601 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 602 "RemoteWorkerChild::ErrorPropagationDispatch", 603 [self = std::move(self), json = nsString(aJSON)]() { 604 CSPViolation violation(json); 605 self->ErrorPropagation(violation); 606 }); 607 608 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 609 } 610 611 void RemoteWorkerChild::NotifyLock(bool aCreated) { 612 nsCOMPtr<nsIRunnable> r = 613 NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] { 614 if (!self->CanSend()) { 615 return; 616 } 617 618 (void)self->SendNotifyLock(aCreated); 619 }); 620 621 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 622 } 623 624 void RemoteWorkerChild::NotifyWebTransport(bool aCreated) { 625 nsCOMPtr<nsIRunnable> r = 626 NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] { 627 if (!self->CanSend()) { 628 return; 629 } 630 631 (void)self->SendNotifyWebTransport(aCreated); 632 }); 633 634 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 635 } 636 637 void RemoteWorkerChild::FlushReportsOnMainThread( 638 nsIConsoleReportCollector* aReporter) { 639 AssertIsOnMainThread(); 640 641 bool reportErrorToBrowserConsole = true; 642 643 // Flush the reports. 644 for (uint32_t i = 0, len = mWindowIDs.Length(); i < len; ++i) { 645 aReporter->FlushReportsToConsole( 646 mWindowIDs[i], nsIConsoleReportCollector::ReportAction::Save); 647 reportErrorToBrowserConsole = false; 648 } 649 650 // Finally report to browser console if there is no any window. 651 if (reportErrorToBrowserConsole) { 652 aReporter->FlushReportsToConsole(0); 653 return; 654 } 655 656 aReporter->ClearConsoleReports(); 657 } 658 659 /** 660 * Worker state transition methods 661 */ 662 void RemoteWorkerChild:: 663 OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled() { 664 auto lock = mState.Lock(); 665 666 LOG(("TransitionStateFromPendingOrRunningToCanceled[this=%p]", this)); 667 668 if (lock->is<Pending>()) { 669 TransitionStateFromPendingToCanceled(lock.ref()); 670 } else if (lock->is<Running>()) { 671 *lock = VariantType<remoteworker::Canceled>(); 672 } else { 673 MOZ_ASSERT(false, "State should have been Pending or Running"); 674 } 675 } 676 677 void RemoteWorkerChild::TransitionStateFromPendingToCanceled( 678 RemoteWorkerState& aState) { 679 AssertIsOnMainThread(); 680 MOZ_ASSERT(aState.is<Pending>()); 681 LOG(("TransitionStateFromPendingToCanceled[this=%p]", this)); 682 683 CancelAllPendingOps(aState); 684 685 aState = VariantType<remoteworker::Canceled>(); 686 } 687 688 void RemoteWorkerChild::TransitionStateFromCanceledToKilled() { 689 AssertIsOnMainThread(); 690 691 LOG(("TransitionStateFromCanceledToKilled[this=%p]", this)); 692 693 auto lock = mState.Lock(); 694 MOZ_ASSERT(lock->is<Canceled>()); 695 696 *lock = VariantType<remoteworker::Killed>(); 697 698 RefPtr<RemoteWorkerChild> self = this; 699 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() { 700 auto launcherData = self->mLauncherData.Access(); 701 702 // (We maintain the historical ordering of resolving this promise prior to 703 // calling SendClose, however the previous code used 2 separate dispatches 704 // to this thread for the resolve and SendClose, and there inherently 705 // would be a race between the runnables resulting from the resolved 706 // promise and the promise containing the call to SendClose. Now it's 707 // entirely clear that our call to SendClose will effectively run before 708 // any of the resolved promises are able to do anything.) 709 launcherData->mTerminationPromise.ResolveIfExists(true, __func__); 710 711 if (self->CanSend()) { 712 (void)self->SendClose(); 713 } 714 }); 715 716 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 717 } 718 719 void RemoteWorkerChild::TransitionStateToRunning() { 720 AssertIsOnMainThread(); 721 722 LOG(("TransitionStateToRunning[this=%p]", this)); 723 724 nsTArray<RefPtr<RemoteWorkerOp>> pendingOps; 725 726 { 727 auto lock = mState.Lock(); 728 729 // Because this is an async notification sent from the worker to the main 730 // thread, it's very possible that we've already decided on the main thread 731 // to transition to the Canceled state, in which case there is nothing for 732 // us to do here. 733 if (!lock->is<Pending>()) { 734 LOG(("State is already not pending in TransitionStateToRunning[this=%p]!", 735 this)); 736 return; 737 } 738 739 RefPtr<WorkerPrivate> workerPrivate = 740 std::move(lock->as<Pending>().mWorkerPrivate); 741 pendingOps = std::move(lock->as<Pending>().mPendingOps); 742 743 // Move the worker private into place to avoid gratuitous ref churn; prior 744 // comments here suggest the Variant can't accept a move. 745 *lock = VariantType<remoteworker::Running>(); 746 lock->as<Running>().mWorkerPrivate = std::move(workerPrivate); 747 } 748 749 CreationSucceededOnAnyThread(); 750 751 RefPtr<RemoteWorkerChild> self = this; 752 for (auto& op : pendingOps) { 753 op->StartOnMainThread(self); 754 } 755 } 756 757 void RemoteWorkerChild::ExceptionalErrorTransitionDuringExecWorker() { 758 AssertIsOnMainThread(); 759 760 LOG(("ExceptionalErrorTransitionDuringExecWorker[this=%p]", this)); 761 762 // This method is called synchronously by ExecWorkerOnMainThread in the event 763 // of any error. Because we only transition to Running on the main thread 764 // as the result of a notification from the worker, we know our state will be 765 // Pending, but mWorkerPrivate may or may not be null, as we may not have 766 // gotten to spawning the worker. 767 // 768 // In the event the worker exists, we need to Cancel() it. We must do this 769 // without the lock held because our call to Cancel() will invoke the 770 // cancellation callback we created which will call TransitionStateToCanceled, 771 // and we can't be holding the lock when that happens. 772 773 RefPtr<WorkerPrivate> cancelWith; 774 775 { 776 auto lock = mState.Lock(); 777 778 MOZ_ASSERT(lock->is<Pending>()); 779 if (lock->is<Pending>()) { 780 cancelWith = lock->as<Pending>().mWorkerPrivate; 781 if (!cancelWith) { 782 // The worker wasn't actually created, so we should synthetically 783 // transition to canceled and onward. Since we have the lock, 784 // perform the transition now for clarity, but we'll handle the rest of 785 // this case after dropping the lock. 786 TransitionStateFromPendingToCanceled(lock.ref()); 787 } 788 } 789 } 790 791 if (cancelWith) { 792 cancelWith->Cancel(); 793 } else { 794 TransitionStateFromCanceledToKilled(); 795 CreationFailedOnAnyThread(); 796 } 797 } 798 799 void RemoteWorkerChild::CancelAllPendingOps(RemoteWorkerState& aState) { 800 MOZ_ASSERT(aState.is<Pending>()); 801 802 auto pendingOps = std::move(aState.as<Pending>().mPendingOps); 803 804 for (auto& op : pendingOps) { 805 op->Cancel(); 806 } 807 } 808 809 void RemoteWorkerChild::PendRemoteWorkerOp(RefPtr<RemoteWorkerOp> aOp) { 810 MOZ_ASSERT_DEBUG_OR_FUZZING(mIsThawing); 811 812 auto pendingOps = mPendingOps.Lock(); 813 814 pendingOps->AppendElement(std::move(aOp)); 815 } 816 817 void RemoteWorkerChild::RunAllPendingOpsOnMainThread() { 818 RefPtr<RemoteWorkerChild> self = this; 819 820 auto pendingOps = mPendingOps.Lock(); 821 822 for (auto& op : *pendingOps) { 823 op->StartOnMainThread(self); 824 } 825 826 pendingOps->Clear(); 827 } 828 829 void RemoteWorkerChild::MaybeStartOp(RefPtr<RemoteWorkerOp>&& aOp) { 830 MOZ_ASSERT(aOp); 831 832 if (mIsThawing) { 833 PendRemoteWorkerOp(std::move(aOp)); 834 return; 835 } 836 837 auto lock = mState.Lock(); 838 839 if (!aOp->MaybeStart(this, lock.ref())) { 840 // Maybestart returns false only if we are <Pending>. 841 lock->as<Pending>().mPendingOps.AppendElement(std::move(aOp)); 842 } 843 } 844 845 IPCResult RemoteWorkerChild::RecvExecOp(SharedWorkerOpArgs&& aOpArgs) { 846 MOZ_ASSERT(!mIsServiceWorker); 847 848 MaybeStartOp(new SharedWorkerOp(std::move(aOpArgs))); 849 850 return IPC_OK(); 851 } 852 853 IPCResult RemoteWorkerChild::RecvExecServiceWorkerOp( 854 ServiceWorkerOpArgs&& aArgs, ExecServiceWorkerOpResolver&& aResolve) { 855 MOZ_ASSERT(mIsServiceWorker); 856 MOZ_ASSERT( 857 aArgs.type() != 858 ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs, 859 "FetchEvent operations should be sent via PFetchEventOp(Proxy) actors!"); 860 861 MaybeReportServiceWorkerShutdownProgress(aArgs); 862 863 MaybeStartOp(ServiceWorkerOp::Create(std::move(aArgs), std::move(aResolve))); 864 865 return IPC_OK(); 866 } 867 868 RefPtr<GenericPromise> 869 RemoteWorkerChild::MaybeSendSetServiceWorkerSkipWaitingFlag() { 870 RefPtr<GenericPromise::Private> promise = 871 new GenericPromise::Private(__func__); 872 873 RefPtr<RemoteWorkerChild> self = this; 874 875 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self = std::move( 876 self), 877 promise] { 878 if (!self->CanSend()) { 879 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 880 return; 881 } 882 883 self->SendSetServiceWorkerSkipWaitingFlag()->Then( 884 GetCurrentSerialEventTarget(), __func__, 885 [promise]( 886 const SetServiceWorkerSkipWaitingFlagPromise::ResolveOrRejectValue& 887 aResult) { 888 if (NS_WARN_IF(aResult.IsReject())) { 889 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 890 return; 891 } 892 893 promise->Resolve(aResult.ResolveValue(), __func__); 894 }); 895 }); 896 897 GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL); 898 899 return promise; 900 } 901 902 /** 903 * PFetchEventOpProxy methods 904 */ 905 already_AddRefed<PFetchEventOpProxyChild> 906 RemoteWorkerChild::AllocPFetchEventOpProxyChild( 907 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) { 908 return RefPtr{new FetchEventOpProxyChild()}.forget(); 909 } 910 911 IPCResult RemoteWorkerChild::RecvPFetchEventOpProxyConstructor( 912 PFetchEventOpProxyChild* aActor, 913 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) { 914 MOZ_ASSERT(aActor); 915 916 (static_cast<FetchEventOpProxyChild*>(aActor))->Initialize(aArgs); 917 918 return IPC_OK(); 919 } 920 921 } // namespace dom 922 } // namespace mozilla