RemoteWorkerController.cpp (18113B)
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 "RemoteWorkerController.h" 8 9 #include <utility> 10 11 #include "RemoteWorkerControllerParent.h" 12 #include "RemoteWorkerManager.h" 13 #include "RemoteWorkerNonLifeCycleOpControllerParent.h" 14 #include "RemoteWorkerParent.h" 15 #include "mozilla/Assertions.h" 16 #include "mozilla/DebugOnly.h" 17 #include "mozilla/Maybe.h" 18 #include "mozilla/RemoteLazyInputStreamStorage.h" 19 #include "mozilla/dom/FetchEventOpParent.h" 20 #include "mozilla/dom/FetchEventOpProxyParent.h" 21 #include "mozilla/dom/MessagePortParent.h" 22 #include "mozilla/dom/RemoteWorkerTypes.h" 23 #include "mozilla/dom/ServiceWorkerCloneData.h" 24 #include "mozilla/dom/ServiceWorkerShutdownState.h" 25 #include "mozilla/ipc/BackgroundParent.h" 26 #include "nsDebug.h" 27 28 namespace mozilla { 29 30 using namespace ipc; 31 32 namespace dom { 33 34 /* static */ 35 already_AddRefed<RemoteWorkerController> RemoteWorkerController::Create( 36 const RemoteWorkerData& aData, RemoteWorkerObserver* aObserver, 37 base::ProcessId aProcessId) { 38 AssertIsInMainProcess(); 39 AssertIsOnBackgroundThread(); 40 MOZ_ASSERT(aObserver); 41 42 RefPtr<RemoteWorkerController> controller = 43 new RemoteWorkerController(aData, aObserver); 44 45 RefPtr<RemoteWorkerManager> manager = RemoteWorkerManager::GetOrCreate(); 46 MOZ_ASSERT(manager); 47 48 // XXX: We do not check for failure here, should we? 49 manager->Launch(controller, aData, aProcessId); 50 51 return controller.forget(); 52 } 53 54 RemoteWorkerController::RemoteWorkerController(const RemoteWorkerData& aData, 55 RemoteWorkerObserver* aObserver) 56 : mObserver(aObserver), 57 mState(ePending), 58 mIsServiceWorker(aData.serviceWorkerData().type() == 59 OptionalServiceWorkerData::TServiceWorkerData) { 60 AssertIsInMainProcess(); 61 AssertIsOnBackgroundThread(); 62 } 63 64 RemoteWorkerController::~RemoteWorkerController() { 65 AssertIsOnBackgroundThread(); 66 MOZ_DIAGNOSTIC_ASSERT(mPendingOps.IsEmpty()); 67 } 68 69 void RemoteWorkerController::SetWorkerActor(RemoteWorkerParent* aActor) { 70 AssertIsOnBackgroundThread(); 71 MOZ_ASSERT(!mActor); 72 MOZ_ASSERT(aActor); 73 74 mActor = aActor; 75 } 76 77 void RemoteWorkerController::NoteDeadWorkerActor() { 78 AssertIsOnBackgroundThread(); 79 MOZ_ASSERT(mActor); 80 81 // The actor has been destroyed without a proper close() notification. Let's 82 // inform the observer. 83 if (mState == eReady) { 84 mObserver->Terminated(); 85 } 86 87 mActor = nullptr; 88 89 Shutdown(); 90 } 91 92 void RemoteWorkerController::CreationFailed() { 93 AssertIsOnBackgroundThread(); 94 MOZ_ASSERT(XRE_IsParentProcess()); 95 MOZ_ASSERT(mState == ePending || mState == eTerminated); 96 97 if (mState == eTerminated) { 98 MOZ_ASSERT(!mActor); 99 MOZ_ASSERT(!mNonLifeCycleOpController); 100 MOZ_ASSERT(mPendingOps.IsEmpty()); 101 // Nothing to do. 102 return; 103 } 104 105 NoteDeadWorker(); 106 107 mObserver->CreationFailed(); 108 } 109 110 void RemoteWorkerController::CreationSucceeded() { 111 AssertIsOnBackgroundThread(); 112 MOZ_ASSERT(mState == ePending || mState == eTerminated); 113 114 if (mState == eTerminated) { 115 MOZ_ASSERT(!mActor); 116 MOZ_ASSERT(!mNonLifeCycleOpController); 117 MOZ_ASSERT(mPendingOps.IsEmpty()); 118 // Nothing to do. 119 return; 120 } 121 122 MOZ_ASSERT(mActor); 123 124 // mNonLifeCycleOpController could be already shutdown at the this moment. 125 // So no need to assert its existence. 126 // op->MaybeStart() will return true to ensure the op will not be in the 127 // mPendingOps 128 129 mState = eReady; 130 131 mObserver->CreationSucceeded(); 132 133 auto pendingOps = std::move(mPendingOps); 134 135 for (auto& op : pendingOps) { 136 DebugOnly<bool> started = op->MaybeStart(this); 137 MOZ_ASSERT(started); 138 } 139 } 140 141 void RemoteWorkerController::ErrorPropagation(const ErrorValue& aValue) { 142 AssertIsOnBackgroundThread(); 143 144 mObserver->ErrorReceived(aValue); 145 } 146 147 void RemoteWorkerController::NotifyLock(bool aCreated) { 148 AssertIsOnBackgroundThread(); 149 150 mObserver->LockNotified(aCreated); 151 } 152 153 void RemoteWorkerController::NotifyWebTransport(bool aCreated) { 154 AssertIsOnBackgroundThread(); 155 156 mObserver->WebTransportNotified(aCreated); 157 } 158 159 void RemoteWorkerController::WorkerTerminated() { 160 AssertIsOnBackgroundThread(); 161 162 NoteDeadWorker(); 163 164 mObserver->Terminated(); 165 } 166 167 void RemoteWorkerController::CancelAllPendingOps() { 168 AssertIsOnBackgroundThread(); 169 170 auto pendingOps = std::move(mPendingOps); 171 172 for (auto& op : pendingOps) { 173 op->Cancel(); 174 } 175 } 176 177 void RemoteWorkerController::Shutdown() { 178 AssertIsOnBackgroundThread(); 179 (void)NS_WARN_IF(mIsServiceWorker && !mPendingOps.IsEmpty()); 180 181 if (mState == eTerminated) { 182 MOZ_ASSERT(mPendingOps.IsEmpty()); 183 return; 184 } 185 186 mState = eTerminated; 187 188 CancelAllPendingOps(); 189 190 if (mNonLifeCycleOpController) { 191 mNonLifeCycleOpController->Shutdown(); 192 mNonLifeCycleOpController = nullptr; 193 } 194 195 if (!mActor) { 196 return; 197 } 198 199 mActor->SetController(nullptr); 200 201 /** 202 * The "non-remote-side" of the Service Worker will have ensured that the 203 * remote worker is terminated before calling `Shutdown().` 204 */ 205 if (mIsServiceWorker) { 206 mActor->MaybeSendDelete(); 207 } else { 208 (void)mActor->SendExecOp(SharedWorkerTerminateOpArgs()); 209 } 210 211 mActor = nullptr; 212 } 213 214 void RemoteWorkerController::NoteDeadWorker() { 215 AssertIsOnBackgroundThread(); 216 217 CancelAllPendingOps(); 218 219 /** 220 * The "non-remote-side" of the Service Worker will initiate `Shutdown()` 221 * once it's notified that all dispatched operations have either completed 222 * or canceled. That is, it'll explicitly call `Shutdown()` later. 223 */ 224 if (!mIsServiceWorker) { 225 Shutdown(); 226 } 227 } 228 229 template <typename... Args> 230 void RemoteWorkerController::MaybeStartSharedWorkerOp(Args&&... aArgs) { 231 AssertIsOnBackgroundThread(); 232 MOZ_ASSERT(!mIsServiceWorker); 233 234 UniquePtr<PendingSharedWorkerOp> op = 235 MakeUnique<PendingSharedWorkerOp>(std::forward<Args>(aArgs)...); 236 237 if (!op->MaybeStart(this)) { 238 mPendingOps.AppendElement(std::move(op)); 239 } 240 } 241 242 void RemoteWorkerController::AddWindowID(uint64_t aWindowID) { 243 AssertIsOnBackgroundThread(); 244 MOZ_ASSERT(aWindowID); 245 246 MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eAddWindowID, aWindowID); 247 } 248 249 void RemoteWorkerController::RemoveWindowID(uint64_t aWindowID) { 250 AssertIsOnBackgroundThread(); 251 MOZ_ASSERT(aWindowID); 252 253 MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eRemoveWindowID, aWindowID); 254 } 255 256 void RemoteWorkerController::AddPortIdentifier( 257 const MessagePortIdentifier& aPortIdentifier) { 258 AssertIsOnBackgroundThread(); 259 260 MaybeStartSharedWorkerOp(aPortIdentifier); 261 } 262 263 void RemoteWorkerController::Terminate() { 264 AssertIsOnBackgroundThread(); 265 266 MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eTerminate); 267 } 268 269 void RemoteWorkerController::Suspend() { 270 AssertIsOnBackgroundThread(); 271 272 MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eSuspend); 273 } 274 275 void RemoteWorkerController::Resume() { 276 AssertIsOnBackgroundThread(); 277 278 MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eResume); 279 } 280 281 void RemoteWorkerController::Freeze() { 282 AssertIsOnBackgroundThread(); 283 284 MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eFreeze); 285 } 286 287 void RemoteWorkerController::Thaw() { 288 AssertIsOnBackgroundThread(); 289 290 MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eThaw); 291 } 292 293 RefPtr<ServiceWorkerOpPromise> RemoteWorkerController::ExecServiceWorkerOp( 294 ServiceWorkerOpArgs&& aArgs) { 295 AssertIsOnBackgroundThread(); 296 MOZ_ASSERT(mIsServiceWorker); 297 298 RefPtr<ServiceWorkerOpPromise::Private> promise = 299 new ServiceWorkerOpPromise::Private(__func__); 300 301 UniquePtr<PendingServiceWorkerOp> op = 302 MakeUnique<PendingServiceWorkerOp>(std::move(aArgs), promise); 303 304 if (!op->MaybeStart(this)) { 305 mPendingOps.AppendElement(std::move(op)); 306 } 307 308 return promise; 309 } 310 311 RefPtr<ServiceWorkerFetchEventOpPromise> 312 RemoteWorkerController::ExecServiceWorkerFetchEventOp( 313 const ParentToParentServiceWorkerFetchEventOpArgs& aArgs, 314 RefPtr<FetchEventOpParent> aReal) { 315 AssertIsOnBackgroundThread(); 316 MOZ_ASSERT(mIsServiceWorker); 317 318 RefPtr<ServiceWorkerFetchEventOpPromise::Private> promise = 319 new ServiceWorkerFetchEventOpPromise::Private(__func__); 320 321 UniquePtr<PendingSWFetchEventOp> op = 322 MakeUnique<PendingSWFetchEventOp>(aArgs, promise, std::move(aReal)); 323 324 if (!op->MaybeStart(this)) { 325 mPendingOps.AppendElement(std::move(op)); 326 } 327 328 return promise; 329 } 330 331 RefPtr<GenericPromise> RemoteWorkerController::SetServiceWorkerSkipWaitingFlag() 332 const { 333 AssertIsOnBackgroundThread(); 334 MOZ_ASSERT(mObserver); 335 336 RefPtr<GenericPromise::Private> promise = 337 new GenericPromise::Private(__func__); 338 339 static_cast<RemoteWorkerControllerParent*>(mObserver.get()) 340 ->MaybeSendSetServiceWorkerSkipWaitingFlag( 341 [promise](bool aOk) { promise->Resolve(aOk, __func__); }); 342 343 return promise; 344 } 345 346 bool RemoteWorkerController::IsTerminated() const { 347 return mState == eTerminated; 348 } 349 350 RemoteWorkerController::PendingSharedWorkerOp::PendingSharedWorkerOp( 351 Type aType, uint64_t aWindowID) 352 : mType(aType), mWindowID(aWindowID) { 353 AssertIsOnBackgroundThread(); 354 } 355 356 RemoteWorkerController::PendingSharedWorkerOp::PendingSharedWorkerOp( 357 const MessagePortIdentifier& aPortIdentifier) 358 : mType(ePortIdentifier), mPortIdentifier(aPortIdentifier) { 359 AssertIsOnBackgroundThread(); 360 } 361 362 RemoteWorkerController::PendingSharedWorkerOp::~PendingSharedWorkerOp() { 363 AssertIsOnBackgroundThread(); 364 MOZ_DIAGNOSTIC_ASSERT(mCompleted); 365 } 366 367 bool RemoteWorkerController::PendingSharedWorkerOp::MaybeStart( 368 RemoteWorkerController* const aOwner) { 369 AssertIsOnBackgroundThread(); 370 MOZ_ASSERT(!mCompleted); 371 MOZ_ASSERT(aOwner); 372 373 if (aOwner->mState == RemoteWorkerController::eTerminated) { 374 Cancel(); 375 return true; 376 } 377 378 if (aOwner->mState == RemoteWorkerController::ePending && 379 mType != eTerminate) { 380 return false; 381 } 382 383 switch (mType) { 384 case eTerminate: 385 aOwner->Shutdown(); 386 break; 387 case eSuspend: 388 (void)aOwner->mActor->SendExecOp(SharedWorkerSuspendOpArgs()); 389 break; 390 case eResume: 391 (void)aOwner->mActor->SendExecOp(SharedWorkerResumeOpArgs()); 392 break; 393 case eFreeze: 394 (void)aOwner->mActor->SendExecOp(SharedWorkerFreezeOpArgs()); 395 break; 396 case eThaw: 397 (void)aOwner->mActor->SendExecOp(SharedWorkerThawOpArgs()); 398 break; 399 case ePortIdentifier: 400 // mNonLifeCycleOpController can be nullptr if the Worker is in "Killing." 401 // RemoteWorkerNonLifeCycleOpControllerChild switches to the Killed status 402 // earlier than RemoteWorkerChild since it switches the status on the 403 // worker thread, not the main thread. 404 if (!aOwner->mNonLifeCycleOpController) { 405 Cancel(); 406 return true; 407 } 408 if (!aOwner->mNonLifeCycleOpController->CanSend()) { 409 return false; 410 } 411 (void)aOwner->mNonLifeCycleOpController->SendExecOp( 412 SharedWorkerPortIdentifierOpArgs(mPortIdentifier)); 413 break; 414 case eAddWindowID: 415 (void)aOwner->mActor->SendExecOp( 416 SharedWorkerAddWindowIDOpArgs(mWindowID)); 417 break; 418 case eRemoveWindowID: 419 (void)aOwner->mActor->SendExecOp( 420 SharedWorkerRemoveWindowIDOpArgs(mWindowID)); 421 break; 422 default: 423 MOZ_CRASH("Unknown op."); 424 } 425 426 mCompleted = true; 427 428 return true; 429 } 430 431 void RemoteWorkerController::PendingSharedWorkerOp::Cancel() { 432 AssertIsOnBackgroundThread(); 433 MOZ_ASSERT(!mCompleted); 434 435 // We don't want to leak the port if the operation has not been processed. 436 if (mType == ePortIdentifier) { 437 MessagePortParent::ForceClose(mPortIdentifier.uuid(), 438 mPortIdentifier.destinationUuid(), 439 mPortIdentifier.sequenceId()); 440 } 441 442 mCompleted = true; 443 } 444 445 RemoteWorkerController::PendingServiceWorkerOp::PendingServiceWorkerOp( 446 ServiceWorkerOpArgs&& aArgs, 447 RefPtr<ServiceWorkerOpPromise::Private> aPromise) 448 : mArgs(std::move(aArgs)), mPromise(std::move(aPromise)) { 449 AssertIsOnBackgroundThread(); 450 MOZ_ASSERT(mPromise); 451 } 452 453 RemoteWorkerController::PendingServiceWorkerOp::~PendingServiceWorkerOp() { 454 AssertIsOnBackgroundThread(); 455 MOZ_DIAGNOSTIC_ASSERT(!mPromise); 456 } 457 458 bool RemoteWorkerController::PendingServiceWorkerOp::MaybeStart( 459 RemoteWorkerController* const aOwner) { 460 AssertIsOnBackgroundThread(); 461 MOZ_ASSERT(mPromise); 462 MOZ_ASSERT(aOwner); 463 464 if (NS_WARN_IF(aOwner->mState == RemoteWorkerController::eTerminated)) { 465 mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 466 mPromise = nullptr; 467 return true; 468 } 469 470 // The target content process must still be starting up. 471 if (!aOwner->mActor) { 472 // We can avoid starting the worker at all if we know it should be 473 // terminated. 474 MOZ_ASSERT(aOwner->mState == RemoteWorkerController::ePending); 475 if (mArgs.type() == 476 ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs) { 477 aOwner->CancelAllPendingOps(); 478 MaybeReportServiceWorkerShutdownProgress(mArgs, true); 479 Cancel(); 480 481 aOwner->mState = RemoteWorkerController::eTerminated; 482 483 return true; 484 } 485 486 return false; 487 } 488 489 /** 490 * Allow termination operations to pass through while pending because the 491 * remote Service Worker can be terminated while still starting up. 492 */ 493 if (aOwner->mState == RemoteWorkerController::ePending && 494 mArgs.type() != 495 ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs) { 496 return false; 497 } 498 499 switch (mArgs.type()) { 500 case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs: 501 case ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs: { 502 MaybeReportServiceWorkerShutdownProgress(mArgs); 503 504 aOwner->mActor->SendExecServiceWorkerOp(mArgs)->Then( 505 GetCurrentSerialEventTarget(), __func__, 506 [promise = std::move(mPromise)]( 507 PRemoteWorkerParent::ExecServiceWorkerOpPromise:: 508 ResolveOrRejectValue&& aResult) { 509 if (NS_WARN_IF(aResult.IsReject())) { 510 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 511 return; 512 } 513 514 promise->Resolve(std::move(aResult.ResolveValue()), __func__); 515 }); 516 break; 517 } 518 default: { 519 // mNonLifeCycleOpController can be nullptr if the Worker is in "Killing." 520 // RemoteWorkerNonLifeCycleOpControllerChild switches to the Killed status 521 // earlier than RemoteWorkerChild since it switches the status on the 522 // worker thread, not the main thread. 523 if (!aOwner->mNonLifeCycleOpController) { 524 Cancel(); 525 return true; 526 } 527 if (!aOwner->mNonLifeCycleOpController->CanSend()) { 528 return false; 529 } 530 aOwner->mNonLifeCycleOpController->SendExecServiceWorkerOp(mArgs)->Then( 531 GetCurrentSerialEventTarget(), __func__, 532 [promise = std::move(mPromise)]( 533 PRemoteWorkerParent::ExecServiceWorkerOpPromise:: 534 ResolveOrRejectValue&& aResult) { 535 if (NS_WARN_IF(aResult.IsReject())) { 536 promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 537 return; 538 } 539 540 promise->Resolve(std::move(aResult.ResolveValue()), __func__); 541 }); 542 } 543 } 544 return true; 545 } 546 547 void RemoteWorkerController::PendingServiceWorkerOp::Cancel() { 548 AssertIsOnBackgroundThread(); 549 MOZ_ASSERT(mPromise); 550 551 mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 552 mPromise = nullptr; 553 } 554 555 RemoteWorkerController::PendingSWFetchEventOp::PendingSWFetchEventOp( 556 const ParentToParentServiceWorkerFetchEventOpArgs& aArgs, 557 RefPtr<ServiceWorkerFetchEventOpPromise::Private> aPromise, 558 RefPtr<FetchEventOpParent>&& aReal) 559 : mArgs(aArgs), mPromise(std::move(aPromise)), mReal(aReal) { 560 AssertIsOnBackgroundThread(); 561 MOZ_ASSERT(mPromise); 562 563 // If there is a TParentToParentStream in the request body, we need to 564 // save it to our stream. 565 IPCInternalRequest& req = mArgs.common().internalRequest(); 566 if (req.body().isSome() && 567 req.body().ref().type() == BodyStreamVariant::TParentToParentStream) { 568 nsCOMPtr<nsIInputStream> stream; 569 auto streamLength = req.bodySize(); 570 const auto& uuid = req.body().ref().get_ParentToParentStream().uuid(); 571 572 auto storage = RemoteLazyInputStreamStorage::Get().unwrapOr(nullptr); 573 MOZ_DIAGNOSTIC_ASSERT(storage); 574 storage->GetStream(uuid, 0, streamLength, getter_AddRefs(mBodyStream)); 575 storage->ForgetStream(uuid); 576 577 MOZ_DIAGNOSTIC_ASSERT(mBodyStream); 578 579 req.body() = Nothing(); 580 } 581 } 582 583 RemoteWorkerController::PendingSWFetchEventOp::~PendingSWFetchEventOp() { 584 AssertIsOnBackgroundThread(); 585 MOZ_DIAGNOSTIC_ASSERT(!mPromise); 586 } 587 588 bool RemoteWorkerController::PendingSWFetchEventOp::MaybeStart( 589 RemoteWorkerController* const aOwner) { 590 AssertIsOnBackgroundThread(); 591 MOZ_ASSERT(mPromise); 592 MOZ_ASSERT(aOwner); 593 594 if (NS_WARN_IF(aOwner->mState == RemoteWorkerController::eTerminated)) { 595 mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 596 mPromise = nullptr; 597 // Because the worker has transitioned to terminated, this operation is moot 598 // and so we should return true because there's no need to queue it. 599 return true; 600 } 601 602 // The target content process must still be starting up. 603 if (!aOwner->mActor) { 604 MOZ_ASSERT(aOwner->mState == RemoteWorkerController::ePending); 605 return false; 606 } 607 608 // At this point we are handing off responsibility for the promise to the 609 // actor. 610 FetchEventOpProxyParent::Create(aOwner->mActor.get(), std::move(mPromise), 611 mArgs, std::move(mReal), 612 std::move(mBodyStream)); 613 614 return true; 615 } 616 617 void RemoteWorkerController::PendingSWFetchEventOp::Cancel() { 618 AssertIsOnBackgroundThread(); 619 MOZ_ASSERT(mPromise); 620 621 if (mPromise) { 622 mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 623 mPromise = nullptr; 624 } 625 } 626 627 } // namespace dom 628 } // namespace mozilla