ServiceWorkerOp.cpp (70181B)
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 "ServiceWorkerOp.h" 8 9 #include <utility> 10 11 #include "ServiceWorkerCloneData.h" 12 #include "ServiceWorkerOpPromise.h" 13 #include "ServiceWorkerShutdownState.h" 14 #include "js/Exception.h" // JS::ExceptionStack, JS::StealPendingExceptionStack 15 #include "jsapi.h" 16 #include "mozilla/Assertions.h" 17 #include "mozilla/CycleCollectedJSContext.h" 18 #include "mozilla/DebugOnly.h" 19 #include "mozilla/ErrorResult.h" 20 #include "mozilla/OwningNonNull.h" 21 #include "mozilla/SchedulerGroup.h" 22 #include "mozilla/ScopeExit.h" 23 #include "mozilla/dom/BindingDeclarations.h" 24 #include "mozilla/dom/Client.h" 25 #include "mozilla/dom/CookieStore.h" 26 #include "mozilla/dom/ExtendableCookieChangeEvent.h" 27 #include "mozilla/dom/ExtendableMessageEventBinding.h" 28 #include "mozilla/dom/FetchEventBinding.h" 29 #include "mozilla/dom/FetchEventOpProxyChild.h" 30 #include "mozilla/dom/InternalHeaders.h" 31 #include "mozilla/dom/InternalRequest.h" 32 #include "mozilla/dom/InternalResponse.h" 33 #include "mozilla/dom/Notification.h" 34 #include "mozilla/dom/NotificationEvent.h" 35 #include "mozilla/dom/NotificationEventBinding.h" 36 #include "mozilla/dom/PerformanceStorage.h" 37 #include "mozilla/dom/PerformanceTiming.h" 38 #include "mozilla/dom/PushEventBinding.h" 39 #include "mozilla/dom/PushSubscriptionChangeEvent.h" 40 #include "mozilla/dom/PushSubscriptionChangeEventBinding.h" 41 #include "mozilla/dom/RemoteWorkerChild.h" 42 #include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.h" 43 #include "mozilla/dom/RemoteWorkerService.h" 44 #include "mozilla/dom/Request.h" 45 #include "mozilla/dom/Response.h" 46 #include "mozilla/dom/RootedDictionary.h" 47 #include "mozilla/dom/SafeRefPtr.h" 48 #include "mozilla/dom/ServiceWorker.h" 49 #include "mozilla/dom/ServiceWorkerBinding.h" 50 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h" 51 #include "mozilla/dom/WorkerCommon.h" 52 #include "mozilla/dom/WorkerRef.h" 53 #include "mozilla/dom/WorkerScope.h" 54 #include "mozilla/extensions/ExtensionBrowser.h" 55 #include "mozilla/ipc/IPCStreamUtils.h" 56 #include "nsCOMPtr.h" 57 #include "nsContentUtils.h" 58 #include "nsDebug.h" 59 #include "nsError.h" 60 #include "nsINamed.h" 61 #include "nsIPushErrorReporter.h" 62 #include "nsISupportsImpl.h" 63 #include "nsITimer.h" 64 #include "nsIURI.h" 65 #include "nsServiceManagerUtils.h" 66 #include "nsTArray.h" 67 #include "nsThreadUtils.h" 68 69 namespace mozilla::dom { 70 71 using remoteworker::Canceled; 72 using remoteworker::Killed; 73 using remoteworker::Pending; 74 using remoteworker::Running; 75 76 namespace { 77 78 class ExtendableEventKeepAliveHandler final 79 : public ExtendableEvent::ExtensionsHandler, 80 public PromiseNativeHandler { 81 public: 82 NS_DECL_ISUPPORTS 83 84 static RefPtr<ExtendableEventKeepAliveHandler> Create( 85 RefPtr<ExtendableEventCallback> aCallback) { 86 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 87 88 RefPtr<ExtendableEventKeepAliveHandler> self = 89 new ExtendableEventKeepAliveHandler(std::move(aCallback)); 90 91 self->mWorkerRef = StrongWorkerRef::Create( 92 GetCurrentThreadWorkerPrivate(), "ExtendableEventKeepAliveHandler", 93 [self]() { self->Cleanup(); }); 94 95 if (NS_WARN_IF(!self->mWorkerRef)) { 96 return nullptr; 97 } 98 99 return self; 100 } 101 102 /** 103 * ExtendableEvent::ExtensionsHandler interface 104 */ 105 bool WaitOnPromise(Promise& aPromise) override { 106 if (!mAcceptingPromises) { 107 MOZ_ASSERT(!GetDispatchFlag()); 108 MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!"); 109 return false; 110 } 111 112 if (!mSelfRef) { 113 MOZ_ASSERT(!mPendingPromisesCount); 114 mSelfRef = this; 115 } 116 117 ++mPendingPromisesCount; 118 aPromise.AppendNativeHandler(this); 119 120 return true; 121 } 122 123 /** 124 * PromiseNativeHandler interface 125 */ 126 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 127 ErrorResult& aRv) override { 128 RemovePromise(Resolved); 129 } 130 131 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 132 ErrorResult& aRv) override { 133 RemovePromise(Rejected); 134 } 135 136 void MaybeDone() { 137 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 138 MOZ_ASSERT(!GetDispatchFlag()); 139 140 if (mPendingPromisesCount) { 141 return; 142 } 143 144 if (mCallback) { 145 mCallback->FinishedWithResult(mRejected ? Rejected : Resolved); 146 mCallback = nullptr; 147 } 148 149 Cleanup(); 150 } 151 152 private: 153 /** 154 * This class is useful for the case where pending microtasks will continue 155 * extending the event, which means that the event is not "done." For example: 156 * 157 * // `e` is an ExtendableEvent, `p` is a Promise 158 * e.waitUntil(p); 159 * p.then(() => e.waitUntil(otherPromise)); 160 */ 161 class MaybeDoneRunner : public MicroTaskRunnable { 162 public: 163 explicit MaybeDoneRunner(RefPtr<ExtendableEventKeepAliveHandler> aHandler) 164 : mHandler(std::move(aHandler)) {} 165 166 void Run(AutoSlowOperation& /* unused */) override { 167 mHandler->MaybeDone(); 168 } 169 170 private: 171 RefPtr<ExtendableEventKeepAliveHandler> mHandler; 172 }; 173 174 explicit ExtendableEventKeepAliveHandler( 175 RefPtr<ExtendableEventCallback> aCallback) 176 : mCallback(std::move(aCallback)) {} 177 178 ~ExtendableEventKeepAliveHandler() { Cleanup(); } 179 180 void Cleanup() { 181 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 182 183 if (mCallback) { 184 mCallback->FinishedWithResult(Rejected); 185 } 186 187 mSelfRef = nullptr; 188 mWorkerRef = nullptr; 189 mCallback = nullptr; 190 mAcceptingPromises = false; 191 } 192 193 void RemovePromise(ExtendableEventResult aResult) { 194 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 195 MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0); 196 197 // NOTE: mSelfRef can be nullptr here if MaybeCleanup() was just called 198 // before a promise settled. This can happen, for example, if the worker 199 // thread is being terminated for running too long, browser shutdown, etc. 200 201 mRejected |= (aResult == Rejected); 202 203 --mPendingPromisesCount; 204 if (mPendingPromisesCount || GetDispatchFlag()) { 205 return; 206 } 207 208 CycleCollectedJSContext* cx = CycleCollectedJSContext::Get(); 209 MOZ_ASSERT(cx); 210 211 RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this); 212 cx->DispatchToMicroTask(r.forget()); 213 } 214 215 /** 216 * We start holding a self reference when the first extension promise is 217 * added, and this reference is released when the last promise settles or 218 * when the worker is shutting down. 219 * 220 * This is needed in the case that we're waiting indefinitely on a to-be-GC'ed 221 * promise that's no longer reachable and will never be settled. 222 */ 223 RefPtr<ExtendableEventKeepAliveHandler> mSelfRef; 224 225 RefPtr<StrongWorkerRef> mWorkerRef; 226 227 RefPtr<ExtendableEventCallback> mCallback; 228 229 uint32_t mPendingPromisesCount = 0; 230 231 bool mRejected = false; 232 bool mAcceptingPromises = true; 233 }; 234 235 NS_IMPL_ISUPPORTS0(ExtendableEventKeepAliveHandler) 236 237 nsresult DispatchExtendableEventOnWorkerScope( 238 JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent, 239 RefPtr<ExtendableEventCallback> aCallback) { 240 MOZ_ASSERT(aCx); 241 MOZ_ASSERT(aWorkerScope); 242 MOZ_ASSERT(aEvent); 243 244 nsCOMPtr<nsIGlobalObject> globalObject = aWorkerScope; 245 WidgetEvent* internalEvent = aEvent->WidgetEventPtr(); 246 247 RefPtr<ExtendableEventKeepAliveHandler> keepAliveHandler = 248 ExtendableEventKeepAliveHandler::Create(std::move(aCallback)); 249 if (NS_WARN_IF(!keepAliveHandler)) { 250 return NS_ERROR_FAILURE; 251 } 252 253 // This must be always set *before* dispatching the event, otherwise 254 // waitUntil() calls will fail. 255 aEvent->SetKeepAliveHandler(keepAliveHandler); 256 257 ErrorResult result; 258 aWorkerScope->DispatchEvent(*aEvent, result); 259 if (NS_WARN_IF(result.Failed())) { 260 result.SuppressException(); 261 return NS_ERROR_FAILURE; 262 } 263 264 keepAliveHandler->MaybeDone(); 265 266 // We don't block the event when getting an exception but still report the 267 // error message. NOTE: this will not stop the event. 268 if (internalEvent->mFlags.mExceptionWasRaised) { 269 return NS_ERROR_XPC_JS_THREW_EXCEPTION; 270 } 271 272 return NS_OK; 273 } 274 275 bool DispatchFailed(nsresult aStatus) { 276 return NS_FAILED(aStatus) && aStatus != NS_ERROR_XPC_JS_THREW_EXCEPTION; 277 } 278 279 } // anonymous namespace 280 281 class ServiceWorkerOp::ServiceWorkerOpRunnable final 282 : public WorkerDebuggeeRunnable { 283 public: 284 NS_DECL_ISUPPORTS_INHERITED 285 286 ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner, 287 WorkerPrivate* aWorkerPrivate) 288 : WorkerDebuggeeRunnable("ServiceWorkerOpRunnable"), 289 mOwner(std::move(aOwner)) { 290 MOZ_ASSERT(mOwner); 291 MOZ_ASSERT(aWorkerPrivate); 292 } 293 294 private: 295 ~ServiceWorkerOpRunnable() = default; 296 297 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 298 MOZ_ASSERT(aWorkerPrivate); 299 aWorkerPrivate->AssertIsOnWorkerThread(); 300 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 301 MOZ_ASSERT(mOwner); 302 303 // GlobalScope could be nullptr here that OOM issue causes GlobalScope 304 // creation fail. 305 if (!aWorkerPrivate->GlobalScope() || 306 aWorkerPrivate->GlobalScope()->IsDying()) { 307 (void)Cancel(); 308 return true; 309 } 310 311 bool rv = mOwner->Exec(aCx, aWorkerPrivate); 312 (void)NS_WARN_IF(!rv); 313 mOwner = nullptr; 314 315 return rv; 316 } 317 318 // Silent PreDispatch and PostDispatch, since ServiceWorkerOpRunnable can be 319 // from the main thread or from the worker thread. 320 bool PreDispatch(WorkerPrivate* WorkerPrivate) override { return true; } 321 void PostDispatch(WorkerPrivate* WorkerPrivate, 322 bool aDispatchResult) override {} 323 324 nsresult Cancel() override { 325 MOZ_ASSERT(mOwner); 326 327 mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR); 328 mOwner = nullptr; 329 330 return NS_OK; 331 } 332 333 RefPtr<ServiceWorkerOp> mOwner; 334 }; 335 336 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable, 337 WorkerThreadRunnable) 338 339 bool ServiceWorkerOp::MaybeStart(RemoteWorkerChild* aOwner, 340 RemoteWorkerState& aState) { 341 MOZ_ASSERT(!mStarted); 342 MOZ_ASSERT(aOwner); 343 MOZ_ASSERT(aOwner->GetActorEventTarget()->IsOnCurrentThread()); 344 345 auto launcherData = aOwner->mLauncherData.Access(); 346 347 if (NS_WARN_IF(!aOwner->CanSend())) { 348 RejectAll(NS_ERROR_DOM_ABORT_ERR); 349 mStarted = true; 350 return true; 351 } 352 353 // Allow termination to happen while the Service Worker is initializing. 354 if (aState.is<Pending>() && !IsTerminationOp()) { 355 return false; 356 } 357 358 if (NS_WARN_IF(aState.is<Canceled>()) || NS_WARN_IF(aState.is<Killed>())) { 359 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR); 360 mStarted = true; 361 return true; 362 } 363 364 MOZ_ASSERT(aState.is<Running>() || IsTerminationOp()); 365 366 RefPtr<ServiceWorkerOp> self = this; 367 368 if (IsTerminationOp()) { 369 aOwner->GetTerminationPromise()->Then( 370 GetCurrentSerialEventTarget(), __func__, 371 [self]( 372 const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) { 373 MaybeReportServiceWorkerShutdownProgress(self->mArgs, true); 374 375 MOZ_ASSERT(!self->mPromiseHolder.IsEmpty()); 376 377 if (NS_WARN_IF(aResult.IsReject())) { 378 self->mPromiseHolder.Reject(aResult.RejectValue(), __func__); 379 return; 380 } 381 382 self->mPromiseHolder.Resolve(NS_OK, __func__); 383 }); 384 } 385 386 // NewRunnableMethod doesn't work here because the template does not appear to 387 // be able to deal with the owner argument having storage as a RefPtr but 388 // with the method taking a RefPtr&. 389 RefPtr<RemoteWorkerChild> owner = aOwner; 390 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 391 __func__, [self = std::move(self), owner = std::move(owner)]() mutable { 392 self->StartOnMainThread(owner); 393 }); 394 395 mStarted = true; 396 397 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 398 return true; 399 } 400 401 void ServiceWorkerOp::StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) { 402 AssertIsOnMainThread(); 403 MaybeReportServiceWorkerShutdownProgress(mArgs); 404 405 { 406 auto lock = aOwner->mState.Lock(); 407 408 if (NS_WARN_IF(!lock->is<Running>() && !IsTerminationOp())) { 409 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR); 410 return; 411 } 412 } 413 414 if (IsTerminationOp()) { 415 aOwner->CloseWorkerOnMainThread(); 416 } else { 417 auto lock = aOwner->mState.Lock(); 418 MOZ_ASSERT(lock->is<Running>()); 419 420 RefPtr<WorkerThreadRunnable> workerRunnable = 421 GetRunnable(lock->as<Running>().mWorkerPrivate); 422 423 if (NS_WARN_IF( 424 !workerRunnable->Dispatch(lock->as<Running>().mWorkerPrivate))) { 425 RejectAll(NS_ERROR_FAILURE); 426 } 427 } 428 } 429 430 void ServiceWorkerOp::Start(RemoteWorkerNonLifeCycleOpControllerChild* aOwner, 431 RemoteWorkerState& aState) { 432 MOZ_ASSERT(!mStarted); 433 MOZ_ASSERT(aOwner); 434 435 if (NS_WARN_IF(!aOwner->CanSend())) { 436 RejectAll(NS_ERROR_DOM_ABORT_ERR); 437 mStarted = true; 438 return; 439 } 440 441 // NonLifeCycle related operations would never start at Pending state. 442 MOZ_ASSERT(!aState.is<Pending>()); 443 444 if (NS_WARN_IF(aState.is<Canceled>()) || NS_WARN_IF(aState.is<Killed>())) { 445 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR); 446 mStarted = true; 447 return; 448 } 449 450 MOZ_ASSERT(aState.is<Running>()); 451 452 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 453 454 MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate); 455 456 RefPtr<WorkerThreadRunnable> workerRunnable = GetRunnable(workerPrivate); 457 458 if (NS_WARN_IF(!workerRunnable->Dispatch(workerPrivate))) { 459 RejectAll(NS_ERROR_FAILURE); 460 } 461 462 mStarted = true; 463 } 464 465 void ServiceWorkerOp::Cancel() { RejectAll(NS_ERROR_DOM_ABORT_ERR); } 466 467 ServiceWorkerOp::ServiceWorkerOp( 468 ServiceWorkerOpArgs&& aArgs, 469 std::function<void(const ServiceWorkerOpResult&)>&& aCallback) 470 : mArgs(std::move(aArgs)) { 471 // Can be on the Worker Launcher thread for Terminate operations or on the 472 // Worker thread for other operations 473 RefPtr<ServiceWorkerOpPromise> promise = mPromiseHolder.Ensure(__func__); 474 475 promise->Then( 476 GetCurrentSerialEventTarget(), __func__, 477 [callback = std::move(aCallback)]( 478 ServiceWorkerOpPromise::ResolveOrRejectValue&& aResult) mutable { 479 if (NS_WARN_IF(aResult.IsReject())) { 480 MOZ_ASSERT(NS_FAILED(aResult.RejectValue())); 481 callback(aResult.RejectValue()); 482 return; 483 } 484 485 callback(aResult.ResolveValue()); 486 }); 487 } 488 489 ServiceWorkerOp::~ServiceWorkerOp() { 490 (void)NS_WARN_IF(!mPromiseHolder.IsEmpty()); 491 mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__); 492 } 493 494 bool ServiceWorkerOp::Started() const { 495 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread()); 496 return mStarted; 497 } 498 499 bool ServiceWorkerOp::IsTerminationOp() const { 500 return mArgs.type() == 501 ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs; 502 } 503 504 RefPtr<WorkerThreadRunnable> ServiceWorkerOp::GetRunnable( 505 WorkerPrivate* aWorkerPrivate) { 506 MOZ_ASSERT(aWorkerPrivate); 507 508 return new ServiceWorkerOpRunnable(this, aWorkerPrivate); 509 } 510 511 void ServiceWorkerOp::RejectAll(nsresult aStatus) { 512 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 513 mPromiseHolder.Reject(aStatus, __func__); 514 } 515 516 class CheckScriptEvaluationOp final : public ServiceWorkerOp { 517 using ServiceWorkerOp::ServiceWorkerOp; 518 519 public: 520 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp, override) 521 522 private: 523 ~CheckScriptEvaluationOp() = default; 524 525 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 526 MOZ_ASSERT(aWorkerPrivate); 527 aWorkerPrivate->AssertIsOnWorkerThread(); 528 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 529 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 530 531 ServiceWorkerCheckScriptEvaluationOpResult result; 532 result.workerScriptExecutedSuccessfully() = 533 aWorkerPrivate->WorkerScriptExecutedSuccessfully(); 534 result.fetchHandlerWasAdded() = aWorkerPrivate->FetchHandlerWasAdded(); 535 536 mPromiseHolder.Resolve(result, __func__); 537 538 return true; 539 } 540 }; 541 542 class TerminateServiceWorkerOp final : public ServiceWorkerOp { 543 using ServiceWorkerOp::ServiceWorkerOp; 544 545 public: 546 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp, override) 547 548 private: 549 ~TerminateServiceWorkerOp() = default; 550 551 bool Exec(JSContext*, WorkerPrivate*) override { 552 MOZ_ASSERT_UNREACHABLE( 553 "Worker termination should be handled in " 554 "`ServiceWorkerOp::MaybeStart()`"); 555 556 return false; 557 } 558 }; 559 560 class UpdateServiceWorkerStateOp final : public ServiceWorkerOp { 561 using ServiceWorkerOp::ServiceWorkerOp; 562 563 public: 564 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp, override); 565 566 private: 567 class UpdateStateOpRunnable final : public WorkerControlRunnable { 568 public: 569 NS_DECL_ISUPPORTS_INHERITED 570 571 UpdateStateOpRunnable(RefPtr<UpdateServiceWorkerStateOp> aOwner, 572 WorkerPrivate* aWorkerPrivate) 573 : WorkerControlRunnable("UpdateStateOpRunnable"), 574 mOwner(std::move(aOwner)) { 575 MOZ_ASSERT(mOwner); 576 MOZ_ASSERT(aWorkerPrivate); 577 aWorkerPrivate->AssertIsOnWorkerThread(); 578 } 579 580 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { 581 aWorkerPrivate->AssertIsOnWorkerThread(); 582 return true; 583 } 584 585 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate, 586 bool aDispatchResult) override { 587 aWorkerPrivate->AssertIsOnWorkerThread(); 588 } 589 590 private: 591 ~UpdateStateOpRunnable() = default; 592 593 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 594 MOZ_ASSERT(aWorkerPrivate); 595 aWorkerPrivate->AssertIsOnWorkerThread(); 596 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 597 598 if (mOwner) { 599 (void)mOwner->Exec(aCx, aWorkerPrivate); 600 mOwner = nullptr; 601 } 602 603 return true; 604 } 605 606 nsresult Cancel() override { 607 MOZ_ASSERT(mOwner); 608 609 mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR); 610 mOwner = nullptr; 611 612 return NS_OK; 613 } 614 615 RefPtr<UpdateServiceWorkerStateOp> mOwner; 616 }; 617 618 ~UpdateServiceWorkerStateOp() = default; 619 620 RefPtr<WorkerThreadRunnable> GetRunnable( 621 WorkerPrivate* aWorkerPrivate) override { 622 MOZ_ASSERT(aWorkerPrivate); 623 aWorkerPrivate->IsOnWorkerThread(); 624 MOZ_ASSERT(mArgs.type() == 625 ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs); 626 627 return new UpdateStateOpRunnable(this, aWorkerPrivate); 628 } 629 630 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 631 MOZ_ASSERT(aWorkerPrivate); 632 aWorkerPrivate->AssertIsOnWorkerThread(); 633 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 634 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 635 636 ServiceWorkerState state = 637 mArgs.get_ServiceWorkerUpdateStateOpArgs().state(); 638 aWorkerPrivate->UpdateServiceWorkerState(state); 639 640 mPromiseHolder.Resolve(NS_OK, __func__); 641 642 return true; 643 } 644 }; 645 646 NS_IMPL_ISUPPORTS_INHERITED0(UpdateServiceWorkerStateOp::UpdateStateOpRunnable, 647 WorkerControlRunnable) 648 649 void ExtendableEventOp::FinishedWithResult(ExtendableEventResult aResult) { 650 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 651 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 652 653 mPromiseHolder.Resolve(aResult == Resolved ? NS_OK : NS_ERROR_FAILURE, 654 __func__); 655 } 656 657 class LifeCycleEventOp final : public ExtendableEventOp { 658 using ExtendableEventOp::ExtendableEventOp; 659 660 public: 661 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp, override) 662 663 private: 664 ~LifeCycleEventOp() = default; 665 666 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 667 MOZ_ASSERT(aWorkerPrivate); 668 aWorkerPrivate->AssertIsOnWorkerThread(); 669 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 670 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 671 672 RefPtr<ExtendableEvent> event; 673 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope(); 674 675 const nsString& eventName = 676 mArgs.get_ServiceWorkerLifeCycleEventOpArgs().eventName(); 677 678 if (eventName.EqualsASCII("install") || eventName.EqualsASCII("activate")) { 679 ExtendableEventInit init; 680 init.mBubbles = false; 681 init.mCancelable = false; 682 event = ExtendableEvent::Constructor(target, eventName, init); 683 } else { 684 MOZ_CRASH("Unexpected lifecycle event"); 685 } 686 687 event->SetTrusted(true); 688 689 nsresult rv = DispatchExtendableEventOnWorkerScope( 690 aCx, aWorkerPrivate->GlobalScope(), event, this); 691 692 if (NS_WARN_IF(DispatchFailed(rv))) { 693 RejectAll(rv); 694 } 695 696 return !DispatchFailed(rv); 697 } 698 }; 699 700 class CookieChangeEventOp final : public ExtendableEventOp { 701 using ExtendableEventOp::ExtendableEventOp; 702 703 public: 704 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CookieChangeEventOp, override) 705 706 private: 707 ~CookieChangeEventOp() = default; 708 709 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 710 MOZ_ASSERT(aWorkerPrivate); 711 aWorkerPrivate->AssertIsOnWorkerThread(); 712 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 713 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 714 715 const ServiceWorkerCookieChangeEventOpArgs& args = 716 mArgs.get_ServiceWorkerCookieChangeEventOpArgs(); 717 718 CookieListItem item; 719 CookieStore::CookieStructToItem(args.cookie(), &item); 720 721 GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper()); 722 nsCOMPtr<EventTarget> eventTarget = 723 do_QueryInterface(globalObj.GetAsSupports()); 724 MOZ_ASSERT(eventTarget); 725 726 RefPtr<ExtendableCookieChangeEvent> event; 727 728 if (args.deleted()) { 729 item.mValue.Reset(); 730 event = ExtendableCookieChangeEvent::CreateForDeletedCookie(eventTarget, 731 item); 732 } else { 733 event = ExtendableCookieChangeEvent::CreateForChangedCookie(eventTarget, 734 item); 735 } 736 737 MOZ_ASSERT(event); 738 739 nsresult rv = DispatchExtendableEventOnWorkerScope( 740 aCx, aWorkerPrivate->GlobalScope(), event, this); 741 742 if (NS_WARN_IF(NS_FAILED(rv))) { 743 return false; 744 } 745 746 return true; 747 } 748 }; 749 750 /** 751 * PushEventOp 752 */ 753 class PushEventOp final : public ExtendableEventOp { 754 using ExtendableEventOp::ExtendableEventOp; 755 756 public: 757 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp, override) 758 759 private: 760 ~PushEventOp() = default; 761 762 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 763 MOZ_ASSERT(aWorkerPrivate); 764 aWorkerPrivate->AssertIsOnWorkerThread(); 765 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 766 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 767 768 ErrorResult result; 769 770 auto scopeExit = MakeScopeExit([&] { 771 MOZ_ASSERT(result.Failed()); 772 773 RejectAll(result.StealNSResult()); 774 ReportError(aWorkerPrivate); 775 }); 776 777 const ServiceWorkerPushEventOpArgs& args = 778 mArgs.get_ServiceWorkerPushEventOpArgs(); 779 780 RootedDictionary<PushEventInit> pushEventInit(aCx); 781 782 if (args.data().type() != OptionalPushData::Tvoid_t) { 783 const auto& bytes = args.data().get_ArrayOfuint8_t(); 784 JSObject* data = Uint8Array::Create(aCx, bytes, result); 785 786 // The ScopeExit above will deal with the exceptions (through 787 // StealNSResult). 788 result.WouldReportJSException(); 789 if (result.Failed()) { 790 return false; 791 } 792 793 DebugOnly<bool> inited = 794 pushEventInit.mData.Construct().SetAsArrayBufferView().Init(data); 795 MOZ_ASSERT(inited); 796 } 797 798 pushEventInit.mBubbles = false; 799 pushEventInit.mCancelable = false; 800 801 GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper()); 802 RefPtr<PushEvent> pushEvent = 803 PushEvent::Constructor(globalObj, u"push"_ns, pushEventInit, result); 804 805 if (NS_WARN_IF(result.Failed())) { 806 return false; 807 } 808 809 pushEvent->SetTrusted(true); 810 811 scopeExit.release(); 812 813 nsresult rv = DispatchExtendableEventOnWorkerScope( 814 aCx, aWorkerPrivate->GlobalScope(), pushEvent, this); 815 816 if (NS_FAILED(rv)) { 817 if (NS_WARN_IF(DispatchFailed(rv))) { 818 RejectAll(rv); 819 } 820 821 // We don't cancel WorkerPrivate when catching an exception. 822 ReportError(aWorkerPrivate, 823 nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION); 824 } 825 826 return !DispatchFailed(rv); 827 } 828 829 void FinishedWithResult(ExtendableEventResult aResult) override { 830 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 831 832 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 833 834 if (aResult == Rejected) { 835 ReportError(workerPrivate, 836 nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION); 837 } 838 839 ExtendableEventOp::FinishedWithResult(aResult); 840 } 841 842 void ReportError( 843 WorkerPrivate* aWorkerPrivate, 844 uint16_t aError = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) { 845 MOZ_ASSERT(aWorkerPrivate); 846 aWorkerPrivate->AssertIsOnWorkerThread(); 847 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 848 849 if (NS_WARN_IF(aError > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) || 850 mArgs.get_ServiceWorkerPushEventOpArgs().messageId().IsEmpty()) { 851 return; 852 } 853 854 nsString messageId = mArgs.get_ServiceWorkerPushEventOpArgs().messageId(); 855 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 856 __func__, [messageId = std::move(messageId), error = aError] { 857 nsCOMPtr<nsIPushErrorReporter> reporter = 858 do_GetService("@mozilla.org/push/Service;1"); 859 860 if (reporter) { 861 nsresult rv = reporter->ReportDeliveryError(messageId, error); 862 (void)NS_WARN_IF(NS_FAILED(rv)); 863 } 864 }); 865 866 MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget())); 867 } 868 }; 869 870 class PushSubscriptionChangeEventOp final : public ExtendableEventOp { 871 using ExtendableEventOp::ExtendableEventOp; 872 873 public: 874 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp, override) 875 876 private: 877 ~PushSubscriptionChangeEventOp() = default; 878 879 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 880 MOZ_ASSERT(aWorkerPrivate); 881 aWorkerPrivate->AssertIsOnWorkerThread(); 882 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 883 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 884 885 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope(); 886 887 ServiceWorkerPushSubscriptionChangeEventOpArgs& args = 888 mArgs.get_ServiceWorkerPushSubscriptionChangeEventOpArgs(); 889 890 PushSubscriptionChangeEventInit init; 891 init.mBubbles = false; 892 init.mCancelable = false; 893 894 if (args.oldSubscription()) { 895 PushSubscriptionData oldSubscriptionData = 896 args.oldSubscription().extract(); 897 RefPtr<PushSubscription> oldSubscription = new PushSubscription( 898 target->GetParentObject(), oldSubscriptionData.endpoint(), u""_ns, 899 Nullable<EpochTimeStamp>(), 900 std::move(oldSubscriptionData.rawP256dhKey()), 901 std::move(oldSubscriptionData.authSecret()), 902 std::move(oldSubscriptionData.appServerKey())); 903 init.mOldSubscription = oldSubscription.forget(); 904 } 905 906 RefPtr<PushSubscriptionChangeEvent> event = 907 PushSubscriptionChangeEvent::Constructor( 908 target, u"pushsubscriptionchange"_ns, init); 909 event->SetTrusted(true); 910 911 nsresult rv = DispatchExtendableEventOnWorkerScope( 912 aCx, aWorkerPrivate->GlobalScope(), event, this); 913 914 if (NS_WARN_IF(DispatchFailed(rv))) { 915 RejectAll(rv); 916 } 917 918 return !DispatchFailed(rv); 919 } 920 }; 921 922 class NotificationEventOp : public ExtendableEventOp, 923 public nsITimerCallback, 924 public nsINamed { 925 using ExtendableEventOp::ExtendableEventOp; 926 927 public: 928 NS_DECL_THREADSAFE_ISUPPORTS 929 930 private: 931 ~NotificationEventOp() { 932 MOZ_DIAGNOSTIC_ASSERT(!mTimer); 933 MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef); 934 } 935 936 void ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) { 937 MOZ_ASSERT(aWorkerPrivate); 938 aWorkerPrivate->AssertIsOnWorkerThread(); 939 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 940 941 if (!mTimer) { 942 return; 943 } 944 945 // This might be executed after the global was unrooted, in which case 946 // GlobalScope() will return null. Making the check here just to be safe. 947 WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope(); 948 if (!globalScope) { 949 return; 950 } 951 952 globalScope->ConsumeWindowInteraction(); 953 mTimer->Cancel(); 954 mTimer = nullptr; 955 956 mWorkerRef = nullptr; 957 } 958 959 void StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) { 960 MOZ_ASSERT(aWorkerPrivate); 961 aWorkerPrivate->AssertIsOnWorkerThread(); 962 MOZ_ASSERT(!mTimer); 963 964 nsresult rv; 965 nsCOMPtr<nsITimer> timer = 966 NS_NewTimer(aWorkerPrivate->ControlEventTarget()); 967 if (NS_WARN_IF(!timer)) { 968 return; 969 } 970 971 MOZ_ASSERT(!mWorkerRef); 972 RefPtr<NotificationEventOp> self = this; 973 mWorkerRef = StrongWorkerRef::Create( 974 aWorkerPrivate, "NotificationEventOp", [self = std::move(self)] { 975 // We could try to hold the worker alive until the timer fires, but 976 // other APIs are not likely to work in this partially shutdown state. 977 // We might as well let the worker thread exit. 978 self->ClearWindowAllowed(self->mWorkerRef->Private()); 979 }); 980 981 if (!mWorkerRef) { 982 return; 983 } 984 985 aWorkerPrivate->GlobalScope()->AllowWindowInteraction(); 986 timer.swap(mTimer); 987 988 // We swap first and then initialize the timer so that even if initializing 989 // fails, we still clean the interaction count correctly. 990 uint32_t delay = 991 StaticPrefs::dom_webnotifications_disable_open_click_delay(); 992 rv = mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT); 993 994 if (NS_WARN_IF(NS_FAILED(rv))) { 995 ClearWindowAllowed(aWorkerPrivate); 996 return; 997 } 998 } 999 1000 // ExtendableEventOp interface 1001 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1002 MOZ_ASSERT(aWorkerPrivate); 1003 aWorkerPrivate->AssertIsOnWorkerThread(); 1004 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 1005 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1006 1007 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope(); 1008 1009 ServiceWorkerNotificationEventOpArgs& args = 1010 mArgs.get_ServiceWorkerNotificationEventOpArgs(); 1011 bool isClick = args.type() == 1012 ServiceWorkerNotificationEventOpArgs:: 1013 TServiceWorkerNotificationClickEventOpArgs; 1014 const IPCNotification& notification = 1015 args.type() == ServiceWorkerNotificationEventOpArgs:: 1016 TServiceWorkerNotificationClickEventOpArgs 1017 ? args.get_ServiceWorkerNotificationClickEventOpArgs() 1018 .notification() 1019 : args.get_ServiceWorkerNotificationCloseEventOpArgs() 1020 .notification(); 1021 1022 auto result = Notification::ConstructFromIPC( 1023 aWorkerPrivate->GlobalScope(), notification, 1024 NS_ConvertUTF8toUTF16(aWorkerPrivate->ServiceWorkerScope())); 1025 1026 if (NS_WARN_IF(result.isErr())) { 1027 return false; 1028 } 1029 1030 NotificationEventInit init; 1031 init.mNotification = result.unwrap(); 1032 init.mBubbles = false; 1033 init.mCancelable = false; 1034 if (isClick) { 1035 init.mAction = 1036 args.get_ServiceWorkerNotificationClickEventOpArgs().action(); 1037 } 1038 1039 RefPtr<NotificationEvent> notificationEvent = 1040 NotificationEvent::Constructor( 1041 target, isClick ? u"notificationclick"_ns : u"notificationclose"_ns, 1042 init); 1043 1044 notificationEvent->SetTrusted(true); 1045 1046 if (isClick) { 1047 StartClearWindowTimer(aWorkerPrivate); 1048 } 1049 1050 nsresult rv = DispatchExtendableEventOnWorkerScope( 1051 aCx, aWorkerPrivate->GlobalScope(), notificationEvent, this); 1052 1053 if (NS_WARN_IF(DispatchFailed(rv))) { 1054 // This will reject mPromiseHolder. 1055 FinishedWithResult(Rejected); 1056 } 1057 1058 return !DispatchFailed(rv); 1059 } 1060 1061 void FinishedWithResult(ExtendableEventResult aResult) override { 1062 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1063 1064 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1065 MOZ_ASSERT(workerPrivate); 1066 1067 ClearWindowAllowed(workerPrivate); 1068 1069 ExtendableEventOp::FinishedWithResult(aResult); 1070 } 1071 1072 // nsITimerCallback interface 1073 NS_IMETHOD Notify(nsITimer* aTimer) override { 1074 MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer); 1075 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1076 ClearWindowAllowed(workerPrivate); 1077 return NS_OK; 1078 } 1079 1080 // nsINamed interface 1081 NS_IMETHOD GetName(nsACString& aName) override { 1082 aName.AssignLiteral("NotificationEventOp"); 1083 return NS_OK; 1084 } 1085 1086 nsCOMPtr<nsITimer> mTimer; 1087 RefPtr<StrongWorkerRef> mWorkerRef; 1088 }; 1089 1090 NS_IMPL_ISUPPORTS(NotificationEventOp, nsITimerCallback, nsINamed) 1091 1092 class MessageEventOp final : public ExtendableEventOp { 1093 using ExtendableEventOp::ExtendableEventOp; 1094 1095 public: 1096 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageEventOp, override) 1097 1098 MessageEventOp(ServiceWorkerOpArgs&& aArgs, 1099 std::function<void(const ServiceWorkerOpResult&)>&& aCallback) 1100 : ExtendableEventOp(std::move(aArgs), std::move(aCallback)), 1101 mData(new ServiceWorkerCloneData()) { 1102 mData->CopyFromClonedMessageData( 1103 mArgs.get_ServiceWorkerMessageEventOpArgs().clonedData()); 1104 } 1105 1106 private: 1107 ~MessageEventOp() = default; 1108 1109 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1110 MOZ_ASSERT(aWorkerPrivate); 1111 aWorkerPrivate->AssertIsOnWorkerThread(); 1112 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 1113 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1114 1115 JS::Rooted<JS::Value> messageData(aCx); 1116 nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope(); 1117 ErrorResult rv; 1118 if (!mData->IsErrorMessageData()) { 1119 mData->Read(aCx, &messageData, rv); 1120 } 1121 1122 // If mData is an error message data, then it means that it failed to 1123 // serialize on the caller side because it contains a shared memory object. 1124 // If deserialization fails, we will fire a messageerror event. 1125 const bool deserializationFailed = 1126 rv.Failed() || mData->IsErrorMessageData(); 1127 1128 Sequence<OwningNonNull<MessagePort>> ports; 1129 if (!mData->TakeTransferredPortsAsSequence(ports)) { 1130 RejectAll(NS_ERROR_FAILURE); 1131 rv.SuppressException(); 1132 return false; 1133 } 1134 1135 RootedDictionary<ExtendableMessageEventInit> init(aCx); 1136 1137 init.mBubbles = false; 1138 init.mCancelable = false; 1139 1140 // On a messageerror event, we disregard ports: 1141 // https://w3c.github.io/ServiceWorker/#service-worker-postmessage 1142 if (!deserializationFailed) { 1143 init.mData = messageData; 1144 init.mPorts = std::move(ports); 1145 } 1146 1147 PostMessageSource& ipcSource = 1148 mArgs.get_ServiceWorkerMessageEventOpArgs().source(); 1149 nsCString originSource; 1150 switch (ipcSource.type()) { 1151 case PostMessageSource::TClientInfoAndState: 1152 originSource = ipcSource.get_ClientInfoAndState().info().url(); 1153 init.mSource.SetValue().SetAsClient() = 1154 new Client(sgo, ipcSource.get_ClientInfoAndState()); 1155 break; 1156 case PostMessageSource::TIPCServiceWorkerDescriptor: 1157 originSource = ipcSource.get_IPCServiceWorkerDescriptor().scriptURL(); 1158 init.mSource.SetValue().SetAsServiceWorker() = ServiceWorker::Create( 1159 sgo, ServiceWorkerDescriptor( 1160 ipcSource.get_IPCServiceWorkerDescriptor())); 1161 break; 1162 default: 1163 MOZ_ASSERT_UNREACHABLE("Unexpected source type"); 1164 return false; 1165 } 1166 1167 nsCOMPtr<nsIURI> url; 1168 nsresult result = NS_NewURI(getter_AddRefs(url), originSource); 1169 if (NS_WARN_IF(NS_FAILED(result))) { 1170 RejectAll(result); 1171 rv.SuppressException(); 1172 return false; 1173 } 1174 1175 OriginAttributes attrs; 1176 nsCOMPtr<nsIPrincipal> principal = 1177 BasePrincipal::CreateContentPrincipal(url, attrs); 1178 if (!principal) { 1179 return false; 1180 } 1181 1182 nsCString origin; 1183 result = principal->GetOriginNoSuffix(origin); 1184 if (NS_WARN_IF(NS_FAILED(result))) { 1185 RejectAll(result); 1186 rv.SuppressException(); 1187 return false; 1188 } 1189 1190 CopyUTF8toUTF16(origin, init.mOrigin); 1191 1192 rv.SuppressException(); 1193 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope(); 1194 RefPtr<ExtendableMessageEvent> extendableEvent = 1195 ExtendableMessageEvent::Constructor( 1196 target, deserializationFailed ? u"messageerror"_ns : u"message"_ns, 1197 init); 1198 1199 extendableEvent->SetTrusted(true); 1200 1201 nsresult rv2 = DispatchExtendableEventOnWorkerScope( 1202 aCx, aWorkerPrivate->GlobalScope(), extendableEvent, this); 1203 1204 if (NS_WARN_IF(DispatchFailed(rv2))) { 1205 RejectAll(rv2); 1206 } 1207 1208 return !DispatchFailed(rv2); 1209 } 1210 1211 RefPtr<ServiceWorkerCloneData> mData; 1212 }; 1213 1214 class UpdateIsOnContentBlockingAllowListOp final : public ExtendableEventOp { 1215 using ExtendableEventOp::ExtendableEventOp; 1216 1217 public: 1218 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateIsOnContentBlockingAllowListOp, 1219 override); 1220 1221 private: 1222 ~UpdateIsOnContentBlockingAllowListOp() = default; 1223 1224 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1225 MOZ_ASSERT(aWorkerPrivate); 1226 aWorkerPrivate->AssertIsOnWorkerThread(); 1227 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 1228 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1229 1230 bool onContentBlockingAllowList = 1231 mArgs.get_ServiceWorkerUpdateIsOnContentBlockingAllowListOpArgs() 1232 .onContentBlockingAllowList(); 1233 aWorkerPrivate->UpdateIsOnContentBlockingAllowList( 1234 onContentBlockingAllowList); 1235 1236 return true; 1237 } 1238 }; 1239 1240 /** 1241 * Used for ScopeExit-style network request cancelation in 1242 * `ResolvedCallback()` (e.g. if `FetchEvent::RespondWith()` is resolved with 1243 * a non-JS object). 1244 */ 1245 class MOZ_STACK_CLASS FetchEventOp::AutoCancel { 1246 public: 1247 explicit AutoCancel(FetchEventOp* aOwner) 1248 : mOwner(aOwner), 1249 mLine(0), 1250 mColumn(0), 1251 mMessageName("InterceptionFailedWithURL"_ns) { 1252 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1253 MOZ_ASSERT(mOwner); 1254 1255 nsAutoString requestURL; 1256 mOwner->GetRequestURL(requestURL); 1257 mParams.AppendElement(requestURL); 1258 } 1259 1260 ~AutoCancel() { 1261 if (mOwner) { 1262 if (mSourceSpec.IsEmpty()) { 1263 mOwner->AsyncLog(mMessageName, std::move(mParams)); 1264 } else { 1265 mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName, 1266 std::move(mParams)); 1267 } 1268 1269 MOZ_ASSERT(!mOwner->mRespondWithPromiseHolder.IsEmpty()); 1270 mOwner->mHandled->MaybeRejectWithNetworkError("AutoCancel"_ns); 1271 mOwner->mRespondWithPromiseHolder.Reject( 1272 CancelInterceptionArgs( 1273 NS_ERROR_INTERCEPTION_FAILED, 1274 FetchEventTimeStamps(mOwner->mFetchHandlerStart, 1275 mOwner->mFetchHandlerFinish)), 1276 __func__); 1277 } 1278 } 1279 1280 // This function steals the error message from a ErrorResult. 1281 void SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) { 1282 MOZ_DIAGNOSTIC_ASSERT(aRv.Failed()); 1283 MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx)); 1284 1285 // Storing the error as exception in the JSContext. 1286 if (!aRv.MaybeSetPendingException(aCx)) { 1287 return; 1288 } 1289 1290 MOZ_ASSERT(!aRv.Failed()); 1291 1292 // Let's take the pending exception. 1293 JS::ExceptionStack exnStack(aCx); 1294 if (!JS::StealPendingExceptionStack(aCx, &exnStack)) { 1295 return; 1296 } 1297 1298 // Converting the exception in a JS::ErrorReportBuilder. 1299 JS::ErrorReportBuilder report(aCx); 1300 if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { 1301 JS_ClearPendingException(aCx); 1302 return; 1303 } 1304 1305 MOZ_ASSERT(mOwner); 1306 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); 1307 MOZ_ASSERT(mParams.Length() == 1); 1308 1309 // Let's store the error message here. 1310 mMessageName.Assign(report.toStringResult().c_str()); 1311 mParams.Clear(); 1312 } 1313 1314 template <typename... Params> 1315 void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) { 1316 MOZ_ASSERT(mOwner); 1317 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); 1318 MOZ_ASSERT(mParams.Length() == 1); 1319 mMessageName = aMessageName; 1320 mParams.Clear(); 1321 StringArrayAppender::Append(mParams, sizeof...(Params), 1322 std::forward<Params>(aParams)...); 1323 } 1324 1325 template <typename... Params> 1326 void SetCancelMessageAndLocation(const nsACString& aSourceSpec, 1327 uint32_t aLine, uint32_t aColumn, 1328 const nsACString& aMessageName, 1329 Params&&... aParams) { 1330 MOZ_ASSERT(mOwner); 1331 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); 1332 MOZ_ASSERT(mParams.Length() == 1); 1333 1334 mSourceSpec = aSourceSpec; 1335 mLine = aLine; 1336 mColumn = aColumn; 1337 1338 mMessageName = aMessageName; 1339 mParams.Clear(); 1340 StringArrayAppender::Append(mParams, sizeof...(Params), 1341 std::forward<Params>(aParams)...); 1342 } 1343 1344 void Reset() { mOwner = nullptr; } 1345 1346 private: 1347 FetchEventOp* MOZ_NON_OWNING_REF mOwner; 1348 nsCString mSourceSpec; 1349 uint32_t mLine; 1350 uint32_t mColumn; 1351 nsCString mMessageName; 1352 nsTArray<nsString> mParams; 1353 }; 1354 1355 NS_IMPL_ISUPPORTS0(FetchEventOp) 1356 1357 void FetchEventOp::SetActor(RefPtr<FetchEventOpProxyChild> aActor) { 1358 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread()); 1359 MOZ_ASSERT(!Started()); 1360 MOZ_ASSERT(!mActor); 1361 1362 mActor = std::move(aActor); 1363 } 1364 1365 void FetchEventOp::RevokeActor(FetchEventOpProxyChild* aActor) { 1366 MOZ_ASSERT(aActor); 1367 MOZ_ASSERT_IF(mActor, mActor == aActor); 1368 1369 mActor = nullptr; 1370 } 1371 1372 RefPtr<FetchEventRespondWithPromise> FetchEventOp::GetRespondWithPromise() { 1373 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread()); 1374 MOZ_ASSERT(!Started()); 1375 MOZ_ASSERT(mRespondWithPromiseHolder.IsEmpty()); 1376 1377 return mRespondWithPromiseHolder.Ensure(__func__); 1378 } 1379 1380 void FetchEventOp::RespondWithCalledAt(const nsCString& aRespondWithScriptSpec, 1381 uint32_t aRespondWithLineNumber, 1382 uint32_t aRespondWithColumnNumber) { 1383 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1384 MOZ_ASSERT(!mRespondWithClosure); 1385 1386 mRespondWithClosure.emplace(aRespondWithScriptSpec, aRespondWithLineNumber, 1387 aRespondWithColumnNumber); 1388 } 1389 1390 void FetchEventOp::ReportCanceled(const nsCString& aPreventDefaultScriptSpec, 1391 uint32_t aPreventDefaultLineNumber, 1392 uint32_t aPreventDefaultColumnNumber) { 1393 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1394 MOZ_ASSERT(mActor); 1395 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1396 1397 nsString requestURL; 1398 GetRequestURL(requestURL); 1399 1400 AsyncLog(aPreventDefaultScriptSpec, aPreventDefaultLineNumber, 1401 aPreventDefaultColumnNumber, "InterceptionCanceledWithURL"_ns, 1402 {std::move(requestURL)}); 1403 } 1404 1405 FetchEventOp::~FetchEventOp() { 1406 mRespondWithPromiseHolder.RejectIfExists( 1407 CancelInterceptionArgs( 1408 NS_ERROR_DOM_ABORT_ERR, 1409 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)), 1410 __func__); 1411 } 1412 1413 void FetchEventOp::RejectAll(nsresult aStatus) { 1414 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty()); 1415 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1416 1417 if (mFetchHandlerStart.IsNull()) { 1418 mFetchHandlerStart = TimeStamp::Now(); 1419 } 1420 if (mFetchHandlerFinish.IsNull()) { 1421 mFetchHandlerFinish = TimeStamp::Now(); 1422 } 1423 1424 mRespondWithPromiseHolder.Reject( 1425 CancelInterceptionArgs( 1426 aStatus, 1427 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)), 1428 __func__); 1429 mPromiseHolder.Reject(aStatus, __func__); 1430 } 1431 1432 void FetchEventOp::FinishedWithResult(ExtendableEventResult aResult) { 1433 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1434 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1435 MOZ_ASSERT(!mResult); 1436 1437 mResult.emplace(aResult); 1438 1439 /** 1440 * This should only return early if neither waitUntil() nor respondWith() 1441 * are called. The early return is so that mRespondWithPromiseHolder has a 1442 * chance to settle before mPromiseHolder does. 1443 */ 1444 if (!mPostDispatchChecksDone) { 1445 return; 1446 } 1447 1448 MaybeFinished(); 1449 } 1450 1451 void FetchEventOp::MaybeFinished() { 1452 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1453 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1454 1455 if (mResult) { 1456 // It's possible that mRespondWithPromiseHolder wasn't settled. That happens 1457 // if the worker was terminated before the respondWith promise settled. 1458 1459 mHandled = nullptr; 1460 mPreloadResponse = nullptr; 1461 mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists(); 1462 mPreloadResponseTimingPromiseRequestHolder.DisconnectIfExists(); 1463 mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists(); 1464 1465 ServiceWorkerFetchEventOpResult result( 1466 mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE); 1467 1468 mPromiseHolder.Resolve(result, __func__); 1469 } 1470 } 1471 1472 bool FetchEventOp::Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { 1473 aWorkerPrivate->AssertIsOnWorkerThread(); 1474 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 1475 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty()); 1476 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1477 1478 nsresult rv = DispatchFetchEvent(aCx, aWorkerPrivate); 1479 1480 if (NS_WARN_IF(NS_FAILED(rv))) { 1481 RejectAll(rv); 1482 } 1483 1484 return NS_SUCCEEDED(rv); 1485 } 1486 1487 void FetchEventOp::AsyncLog(const nsCString& aMessageName, 1488 nsTArray<nsString> aParams) { 1489 MOZ_ASSERT(mActor); 1490 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1491 MOZ_ASSERT(mRespondWithClosure); 1492 1493 const FetchEventRespondWithClosure& closure = mRespondWithClosure.ref(); 1494 1495 AsyncLog(closure.respondWithScriptSpec(), closure.respondWithLineNumber(), 1496 closure.respondWithColumnNumber(), aMessageName, std::move(aParams)); 1497 } 1498 1499 void FetchEventOp::AsyncLog(const nsCString& aScriptSpec, uint32_t aLineNumber, 1500 uint32_t aColumnNumber, 1501 const nsCString& aMessageName, 1502 nsTArray<nsString> aParams) { 1503 MOZ_ASSERT(mActor); 1504 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1505 1506 // Capture `this` because FetchEventOpProxyChild (mActor) is not thread 1507 // safe, so an AddRef from RefPtr<FetchEventOpProxyChild>'s constructor will 1508 // assert. 1509 RefPtr<FetchEventOp> self = this; 1510 1511 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 1512 __func__, [self = std::move(self), spec = aScriptSpec, line = aLineNumber, 1513 column = aColumnNumber, messageName = aMessageName, 1514 params = std::move(aParams)] { 1515 if (NS_WARN_IF(!self->mActor)) { 1516 return; 1517 } 1518 1519 (void)self->mActor->SendAsyncLog(spec, line, column, messageName, 1520 params); 1521 }); 1522 1523 MOZ_ALWAYS_SUCCEEDS( 1524 RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL)); 1525 } 1526 1527 void FetchEventOp::GetRequestURL(nsAString& aOutRequestURL) { 1528 nsTArray<nsCString>& urls = 1529 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs() 1530 .common() 1531 .internalRequest() 1532 .urlList(); 1533 MOZ_ASSERT(!urls.IsEmpty()); 1534 1535 CopyUTF8toUTF16(urls.LastElement(), aOutRequestURL); 1536 } 1537 1538 void FetchEventOp::ResolvedCallback(JSContext* aCx, 1539 JS::Handle<JS::Value> aValue, 1540 ErrorResult& aRv) { 1541 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1542 MOZ_ASSERT(mRespondWithClosure); 1543 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty()); 1544 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1545 1546 mFetchHandlerFinish = TimeStamp::Now(); 1547 1548 nsAutoString requestURL; 1549 GetRequestURL(requestURL); 1550 1551 AutoCancel autoCancel(this); 1552 1553 if (!aValue.isObject()) { 1554 NS_WARNING( 1555 "FetchEvent::RespondWith was passed a promise resolved to a " 1556 "non-Object " 1557 "value"); 1558 1559 nsCString sourceSpec; 1560 uint32_t line = 0; 1561 uint32_t column = 0; 1562 nsString valueString; 1563 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, 1564 valueString); 1565 1566 autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, 1567 "InterceptedNonResponseWithURL"_ns, 1568 requestURL, valueString); 1569 return; 1570 } 1571 1572 RefPtr<Response> response; 1573 nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); 1574 if (NS_FAILED(rv)) { 1575 nsCString sourceSpec; 1576 uint32_t line = 0; 1577 uint32_t column = 0; 1578 nsString valueString; 1579 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, 1580 valueString); 1581 1582 autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, 1583 "InterceptedNonResponseWithURL"_ns, 1584 requestURL, valueString); 1585 return; 1586 } 1587 1588 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); 1589 MOZ_ASSERT(worker); 1590 worker->AssertIsOnWorkerThread(); 1591 1592 // Section "HTTP Fetch", step 3.3: 1593 // If one of the following conditions is true, return a network error: 1594 // * response's type is "error". 1595 // * request's mode is not "no-cors" and response's type is "opaque". 1596 // * request's redirect mode is not "manual" and response's type is 1597 // "opaqueredirect". 1598 // * request's redirect mode is not "follow" and response's url list 1599 // has more than one item. 1600 1601 if (response->Type() == ResponseType::Error) { 1602 autoCancel.SetCancelMessage("InterceptedErrorResponseWithURL"_ns, 1603 requestURL); 1604 return; 1605 } 1606 1607 const ParentToChildServiceWorkerFetchEventOpArgs& args = 1608 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs(); 1609 const RequestMode requestMode = args.common().internalRequest().requestMode(); 1610 1611 if (response->Type() == ResponseType::Opaque && 1612 requestMode != RequestMode::No_cors) { 1613 NS_ConvertASCIItoUTF16 modeString(GetEnumString(requestMode)); 1614 1615 nsAutoString requestURL; 1616 GetRequestURL(requestURL); 1617 1618 autoCancel.SetCancelMessage("BadOpaqueInterceptionRequestModeWithURL"_ns, 1619 requestURL, modeString); 1620 return; 1621 } 1622 1623 const RequestRedirect requestRedirectMode = 1624 args.common().internalRequest().requestRedirect(); 1625 1626 if (requestRedirectMode != RequestRedirect::Manual && 1627 response->Type() == ResponseType::Opaqueredirect) { 1628 autoCancel.SetCancelMessage("BadOpaqueRedirectInterceptionWithURL"_ns, 1629 requestURL); 1630 return; 1631 } 1632 1633 if (requestRedirectMode != RequestRedirect::Follow && 1634 response->Redirected()) { 1635 autoCancel.SetCancelMessage("BadRedirectModeInterceptionWithURL"_ns, 1636 requestURL); 1637 return; 1638 } 1639 1640 if (NS_WARN_IF(response->BodyUsed())) { 1641 autoCancel.SetCancelMessage("InterceptedUsedResponseWithURL"_ns, 1642 requestURL); 1643 return; 1644 } 1645 1646 SafeRefPtr<InternalResponse> ir = response->GetInternalResponse(); 1647 if (NS_WARN_IF(!ir)) { 1648 return; 1649 } 1650 1651 // An extra safety check to make sure our invariant that opaque and cors 1652 // responses always have a URL does not break. 1653 if (NS_WARN_IF((response->Type() == ResponseType::Opaque || 1654 response->Type() == ResponseType::Cors) && 1655 ir->GetUnfilteredURL().IsEmpty())) { 1656 MOZ_DIAGNOSTIC_CRASH("Cors or opaque Response without a URL"); 1657 return; 1658 } 1659 1660 if (requestMode == RequestMode::Same_origin && 1661 response->Type() == ResponseType::Cors) { 1662 // XXXtt: Will have a pref to enable the quirk response in bug 1419684. 1663 // The variadic template provided by StringArrayAppender requires exactly 1664 // an nsString. 1665 NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL()); 1666 autoCancel.SetCancelMessage("CorsResponseForSameOriginRequest"_ns, 1667 requestURL, responseURL); 1668 return; 1669 } 1670 1671 nsCOMPtr<nsIInputStream> body; 1672 ir->GetUnfilteredBody(getter_AddRefs(body)); 1673 // Errors and redirects may not have a body. 1674 if (body) { 1675 ErrorResult error; 1676 response->SetBodyUsed(aCx, error); 1677 error.WouldReportJSException(); 1678 if (NS_WARN_IF(error.Failed())) { 1679 autoCancel.SetCancelErrorResult(aCx, error); 1680 return; 1681 } 1682 } 1683 1684 if (!ir->GetChannelInfo().IsInitialized()) { 1685 // This is a synthetic response (I think and hope so). 1686 ir->InitChannelInfo(worker->GetChannelInfo()); 1687 } 1688 1689 autoCancel.Reset(); 1690 1691 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 26: If 1692 // eventHandled is not null, then resolve eventHandled. 1693 // 1694 // mRespondWithPromiseHolder will resolve a MozPromise that will resolve on 1695 // the worker owner's thread, so it's fine to resolve the mHandled promise now 1696 // because content will not interfere with respondWith getting the Response to 1697 // where it's going. 1698 mHandled->MaybeResolveWithUndefined(); 1699 mRespondWithPromiseHolder.Resolve( 1700 FetchEventRespondWithResult(std::make_tuple( 1701 std::move(ir), mRespondWithClosure.ref(), 1702 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))), 1703 __func__); 1704 } 1705 1706 void FetchEventOp::RejectedCallback(JSContext* aCx, 1707 JS::Handle<JS::Value> aValue, 1708 ErrorResult& aRv) { 1709 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1710 MOZ_ASSERT(mRespondWithClosure); 1711 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty()); 1712 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 1713 1714 mFetchHandlerFinish = TimeStamp::Now(); 1715 1716 FetchEventRespondWithClosure& closure = mRespondWithClosure.ref(); 1717 1718 nsCString sourceSpec = closure.respondWithScriptSpec(); 1719 uint32_t line = closure.respondWithLineNumber(); 1720 uint32_t column = closure.respondWithColumnNumber(); 1721 nsString valueString; 1722 1723 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, 1724 valueString); 1725 1726 nsString requestURL; 1727 GetRequestURL(requestURL); 1728 1729 AsyncLog(sourceSpec, line, column, "InterceptionRejectedResponseWithURL"_ns, 1730 {std::move(requestURL), valueString}); 1731 1732 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 25.1: 1733 // If eventHandled is not null, then reject eventHandled with a "NetworkError" 1734 // DOMException in workerRealm. 1735 mHandled->MaybeRejectWithNetworkError( 1736 "FetchEvent.respondWith() Promise rejected"_ns); 1737 mRespondWithPromiseHolder.Resolve( 1738 FetchEventRespondWithResult(CancelInterceptionArgs( 1739 NS_ERROR_INTERCEPTION_FAILED, 1740 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))), 1741 __func__); 1742 } 1743 1744 nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx, 1745 WorkerPrivate* aWorkerPrivate) { 1746 MOZ_ASSERT(aCx); 1747 MOZ_ASSERT(aWorkerPrivate); 1748 aWorkerPrivate->AssertIsOnWorkerThread(); 1749 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 1750 1751 ParentToChildServiceWorkerFetchEventOpArgs& args = 1752 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs(); 1753 1754 /** 1755 * Testing: Failure injection. 1756 * 1757 * There are a number of different ways that this fetch event could have 1758 * failed that would result in cancellation. This injection point helps 1759 * simulate them without worrying about shifting implementation details with 1760 * full fidelity reproductions of current scenarios. 1761 * 1762 * Broadly speaking, we expect fetch event scenarios to fail because of: 1763 * - Script load failure, which results in the CompileScriptRunnable closing 1764 * the worker and thereby cancelling all pending operations, including this 1765 * fetch. The `ServiceWorkerOp::Cancel` impl just calls 1766 * RejectAll(NS_ERROR_DOM_ABORT_ERR) which we are able to approximate by 1767 * returning the same nsresult here, as our caller also calls RejectAll. 1768 * (And timing-wise, this rejection will happen in the correct sequence.) 1769 * - An exception gets thrown in the processing of the promise that was passed 1770 * to respondWith and it ends up rejecting. The rejection will be converted 1771 * by `FetchEventOp::RejectedCallback` into a cancellation with 1772 * NS_ERROR_INTERCEPTION_FAILED, and by returning that here we approximate 1773 * that failure mode. 1774 */ 1775 if (NS_FAILED(args.common().testingInjectCancellation())) { 1776 return args.common().testingInjectCancellation(); 1777 } 1778 1779 /** 1780 * Step 1: get the InternalRequest. The InternalRequest can't be constructed 1781 * here from mArgs because the IPCStream has to be deserialized on the 1782 * thread receiving the ServiceWorkerFetchEventOpArgs. 1783 * FetchEventOpProxyChild will have already deserialized the stream on the 1784 * correct thread before creating this op, so we can take its saved 1785 * InternalRequest. 1786 */ 1787 SafeRefPtr<InternalRequest> internalRequest = 1788 mActor->ExtractInternalRequest(); 1789 1790 /** 1791 * Step 2: get the worker's global object 1792 */ 1793 GlobalObject globalObject(aCx, aWorkerPrivate->GlobalScope()->GetWrapper()); 1794 nsCOMPtr<nsIGlobalObject> globalObjectAsSupports = 1795 do_QueryInterface(globalObject.GetAsSupports()); 1796 if (NS_WARN_IF(!globalObjectAsSupports)) { 1797 return NS_ERROR_DOM_INVALID_STATE_ERR; 1798 } 1799 1800 /** 1801 * Step 3: create the public DOM Request object 1802 * TODO: this Request object should be created with an AbortSignal object 1803 * which should be aborted if the loading is aborted. See but 1394102. 1804 */ 1805 RefPtr<Request> request = 1806 new Request(globalObjectAsSupports, internalRequest.clonePtr(), nullptr); 1807 MOZ_ASSERT_IF(internalRequest->IsNavigationRequest(), 1808 request->Redirect() == RequestRedirect::Manual); 1809 1810 /** 1811 * Step 4a: create the FetchEventInit 1812 */ 1813 RootedDictionary<FetchEventInit> fetchEventInit(aCx); 1814 fetchEventInit.mRequest = request; 1815 fetchEventInit.mBubbles = false; 1816 fetchEventInit.mCancelable = true; 1817 1818 /** 1819 * TODO: only expose the FetchEvent.clientId on subresource requests for 1820 * now. Once we implement .targetClientId we can then start exposing 1821 * .clientId on non-subresource requests as well. See bug 1487534. 1822 */ 1823 if (!args.common().clientId().IsEmpty() && 1824 !internalRequest->IsNavigationRequest()) { 1825 fetchEventInit.mClientId = args.common().clientId(); 1826 } 1827 1828 /* 1829 * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm 1830 * 1831 * "If request is a non-subresource request and request’s 1832 * destination is not "report", initialize e’s resultingClientId attribute 1833 * to reservedClient’s [resultingClient's] id, and to the empty string 1834 * otherwise." (Step 18.8) 1835 */ 1836 if (!args.common().resultingClientId().IsEmpty() && 1837 args.common().isNonSubresourceRequest() && 1838 internalRequest->Destination() != RequestDestination::Report) { 1839 fetchEventInit.mResultingClientId = args.common().resultingClientId(); 1840 } 1841 1842 /** 1843 * Step 4b: create the FetchEvent 1844 */ 1845 RefPtr<FetchEvent> fetchEvent = 1846 FetchEvent::Constructor(globalObject, u"fetch"_ns, fetchEventInit); 1847 fetchEvent->SetTrusted(true); 1848 fetchEvent->PostInit(args.common().workerScriptSpec(), this); 1849 mHandled = fetchEvent->Handled(); 1850 mPreloadResponse = fetchEvent->PreloadResponse(); 1851 1852 if (args.common().preloadNavigation()) { 1853 RefPtr<FetchEventPreloadResponseAvailablePromise> preloadResponsePromise = 1854 mActor->GetPreloadResponseAvailablePromise(); 1855 MOZ_ASSERT(preloadResponsePromise); 1856 1857 // If preloadResponsePromise has already settled then this callback will get 1858 // run synchronously here. 1859 RefPtr<FetchEventOp> self = this; 1860 preloadResponsePromise 1861 ->Then( 1862 GetCurrentSerialEventTarget(), __func__, 1863 [self, globalObjectAsSupports]( 1864 SafeRefPtr<InternalResponse>&& aPreloadResponse) { 1865 self->mPreloadResponse->MaybeResolve( 1866 MakeRefPtr<Response>(globalObjectAsSupports, 1867 std::move(aPreloadResponse), nullptr)); 1868 self->mPreloadResponseAvailablePromiseRequestHolder.Complete(); 1869 }, 1870 [self](int) { 1871 self->mPreloadResponseAvailablePromiseRequestHolder.Complete(); 1872 }) 1873 ->Track(mPreloadResponseAvailablePromiseRequestHolder); 1874 1875 RefPtr<PerformanceStorage> performanceStorage = 1876 aWorkerPrivate->GetPerformanceStorage(); 1877 1878 RefPtr<FetchEventPreloadResponseTimingPromise> 1879 preloadResponseTimingPromise = 1880 mActor->GetPreloadResponseTimingPromise(); 1881 MOZ_ASSERT(preloadResponseTimingPromise); 1882 preloadResponseTimingPromise 1883 ->Then( 1884 GetCurrentSerialEventTarget(), __func__, 1885 [self, performanceStorage, 1886 globalObjectAsSupports](ResponseTiming&& aTiming) { 1887 if (performanceStorage && !aTiming.entryName().IsEmpty() && 1888 aTiming.initiatorType().Equals(u"navigation"_ns)) { 1889 performanceStorage->AddEntry( 1890 aTiming.entryName(), aTiming.initiatorType(), 1891 MakeUnique<PerformanceTimingData>(aTiming.timingData())); 1892 } 1893 self->mPreloadResponseTimingPromiseRequestHolder.Complete(); 1894 }, 1895 [self](int) { 1896 self->mPreloadResponseTimingPromiseRequestHolder.Complete(); 1897 }) 1898 ->Track(mPreloadResponseTimingPromiseRequestHolder); 1899 1900 RefPtr<FetchEventPreloadResponseEndPromise> preloadResponseEndPromise = 1901 mActor->GetPreloadResponseEndPromise(); 1902 MOZ_ASSERT(preloadResponseEndPromise); 1903 preloadResponseEndPromise 1904 ->Then( 1905 GetCurrentSerialEventTarget(), __func__, 1906 [self, globalObjectAsSupports](ResponseEndArgs&& aArgs) { 1907 if (aArgs.endReason() == FetchDriverObserver::eAborted) { 1908 self->mPreloadResponse->MaybeReject(NS_ERROR_DOM_ABORT_ERR); 1909 } 1910 self->mPreloadResponseEndPromiseRequestHolder.Complete(); 1911 }, 1912 [self](int) { 1913 self->mPreloadResponseEndPromiseRequestHolder.Complete(); 1914 }) 1915 ->Track(mPreloadResponseEndPromiseRequestHolder); 1916 } else { 1917 // preload navigation is disabled, resolved preload response promise with 1918 // undefined as default behavior. 1919 mPreloadResponse->MaybeResolveWithUndefined(); 1920 } 1921 1922 mFetchHandlerStart = TimeStamp::Now(); 1923 1924 /** 1925 * Step 5: Dispatch the FetchEvent to the worker's global object 1926 */ 1927 nsresult rv = DispatchExtendableEventOnWorkerScope( 1928 aCx, aWorkerPrivate->GlobalScope(), fetchEvent, this); 1929 bool dispatchFailed = NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION; 1930 1931 if (NS_WARN_IF(dispatchFailed)) { 1932 mHandled = nullptr; 1933 mPreloadResponse = nullptr; 1934 return rv; 1935 } 1936 1937 /** 1938 * At this point, there are 4 (legal) scenarios: 1939 * 1940 * 1) If neither waitUntil() nor respondWith() are called, 1941 * DispatchExtendableEventOnWorkerScope() will have already called 1942 * FinishedWithResult(), but this call will have recorded the result 1943 * (mResult) and returned early so that mRespondWithPromiseHolder can be 1944 * settled first. mRespondWithPromiseHolder will be settled below, followed 1945 * by a call to MaybeFinished() which settles mPromiseHolder. 1946 * 1947 * 2) If waitUntil() is called at least once, and respondWith() is not 1948 * called, DispatchExtendableEventOnWorkerScope() will NOT have called 1949 * FinishedWithResult(). We'll settle mRespondWithPromiseHolder first, and 1950 * at some point in the future when the last waitUntil() promise settles, 1951 * FinishedWithResult() will be called, settling mPromiseHolder. 1952 * 1953 * 3) If waitUntil() is not called, and respondWith() is called, 1954 * DispatchExtendableEventOnWorkerScope() will NOT have called 1955 * FinishedWithResult(). We can also guarantee that 1956 * mRespondWithPromiseHolder will be settled before mPromiseHolder, due to 1957 * the Promise::AppendNativeHandler() call ordering in 1958 * FetchEvent::RespondWith(). 1959 * 1960 * 4) If waitUntil() is called at least once, and respondWith() is also 1961 * called, the effect is similar to scenario 3), with the most imporant 1962 * property being mRespondWithPromiseHolder settling before mPromiseHolder. 1963 * 1964 * Note that if mPromiseHolder is settled before mRespondWithPromiseHolder, 1965 * FetchEventOpChild will cancel the interception. 1966 */ 1967 if (!fetchEvent->WaitToRespond()) { 1968 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty()); 1969 MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(), 1970 "We don't support system-principal serviceworkers"); 1971 1972 mFetchHandlerFinish = TimeStamp::Now(); 1973 1974 if (fetchEvent->DefaultPrevented(CallerType::NonSystem)) { 1975 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm 1976 // Step 24.1.1: If eventHandled is not null, then reject eventHandled with 1977 // a "NetworkError" DOMException in workerRealm. 1978 mHandled->MaybeRejectWithNetworkError( 1979 "FetchEvent.preventDefault() called"_ns); 1980 mRespondWithPromiseHolder.Resolve( 1981 FetchEventRespondWithResult(CancelInterceptionArgs( 1982 NS_ERROR_INTERCEPTION_FAILED, 1983 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))), 1984 __func__); 1985 } else { 1986 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm 1987 // Step 24.2: If eventHandled is not null, then resolve eventHandled. 1988 mHandled->MaybeResolveWithUndefined(); 1989 mRespondWithPromiseHolder.Resolve( 1990 FetchEventRespondWithResult(ResetInterceptionArgs( 1991 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))), 1992 __func__); 1993 } 1994 } else { 1995 MOZ_ASSERT(mRespondWithClosure); 1996 } 1997 1998 mPostDispatchChecksDone = true; 1999 MaybeFinished(); 2000 2001 return NS_OK; 2002 } 2003 2004 class ExtensionAPIEventOp final : public ServiceWorkerOp { 2005 using ServiceWorkerOp::ServiceWorkerOp; 2006 2007 public: 2008 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExtensionAPIEventOp, override) 2009 2010 private: 2011 ~ExtensionAPIEventOp() = default; 2012 2013 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 2014 MOZ_ASSERT(aWorkerPrivate); 2015 aWorkerPrivate->AssertIsOnWorkerThread(); 2016 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); 2017 MOZ_ASSERT(aWorkerPrivate->ExtensionAPIAllowed()); 2018 MOZ_ASSERT(!mPromiseHolder.IsEmpty()); 2019 2020 ServiceWorkerExtensionAPIEventOpArgs& args = 2021 mArgs.get_ServiceWorkerExtensionAPIEventOpArgs(); 2022 2023 ServiceWorkerExtensionAPIEventOpResult result; 2024 result.extensionAPIEventListenerWasAdded() = false; 2025 2026 if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) { 2027 GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper()); 2028 RefPtr<ServiceWorkerGlobalScope> scope; 2029 UNWRAP_OBJECT(ServiceWorkerGlobalScope, globalObj.Get(), scope); 2030 SafeRefPtr<extensions::ExtensionBrowser> extensionAPI = 2031 scope->AcquireExtensionBrowser(); 2032 if (!extensionAPI) { 2033 // If the worker script did never access the WebExtension APIs 2034 // then we can return earlier, no event listener could have been added. 2035 mPromiseHolder.Resolve(result, __func__); 2036 return true; 2037 } 2038 // Check if a listener has been subscribed on the expected WebExtensions 2039 // API event. 2040 bool hasWakeupListener = extensionAPI->HasWakeupEventListener( 2041 args.apiNamespace(), args.apiEventName()); 2042 result.extensionAPIEventListenerWasAdded() = hasWakeupListener; 2043 mPromiseHolder.Resolve(result, __func__); 2044 } else { 2045 mPromiseHolder.Resolve(result, __func__); 2046 } 2047 2048 return true; 2049 } 2050 }; 2051 2052 /* static */ already_AddRefed<ServiceWorkerOp> ServiceWorkerOp::Create( 2053 ServiceWorkerOpArgs&& aArgs, 2054 std::function<void(const ServiceWorkerOpResult&)>&& aCallback) { 2055 // Can be on the Worker Launcher thread for Terminate operations or on the 2056 // Worker thread for other operations. 2057 RefPtr<ServiceWorkerOp> op; 2058 2059 switch (aArgs.type()) { 2060 case ServiceWorkerOpArgs::TServiceWorkerCheckScriptEvaluationOpArgs: 2061 op = MakeRefPtr<CheckScriptEvaluationOp>(std::move(aArgs), 2062 std::move(aCallback)); 2063 break; 2064 case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs: 2065 op = MakeRefPtr<UpdateServiceWorkerStateOp>(std::move(aArgs), 2066 std::move(aCallback)); 2067 break; 2068 case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs: 2069 op = MakeRefPtr<TerminateServiceWorkerOp>(std::move(aArgs), 2070 std::move(aCallback)); 2071 break; 2072 case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs: 2073 op = MakeRefPtr<LifeCycleEventOp>(std::move(aArgs), std::move(aCallback)); 2074 break; 2075 case ServiceWorkerOpArgs::TServiceWorkerCookieChangeEventOpArgs: 2076 op = MakeRefPtr<CookieChangeEventOp>(std::move(aArgs), 2077 std::move(aCallback)); 2078 break; 2079 case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs: 2080 op = MakeRefPtr<PushEventOp>(std::move(aArgs), std::move(aCallback)); 2081 break; 2082 case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs: 2083 op = MakeRefPtr<PushSubscriptionChangeEventOp>(std::move(aArgs), 2084 std::move(aCallback)); 2085 break; 2086 case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs: 2087 op = MakeRefPtr<NotificationEventOp>(std::move(aArgs), 2088 std::move(aCallback)); 2089 break; 2090 case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs: 2091 op = MakeRefPtr<MessageEventOp>(std::move(aArgs), std::move(aCallback)); 2092 break; 2093 case ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs: 2094 op = MakeRefPtr<FetchEventOp>(std::move(aArgs), std::move(aCallback)); 2095 break; 2096 case ServiceWorkerOpArgs::TServiceWorkerExtensionAPIEventOpArgs: 2097 op = MakeRefPtr<ExtensionAPIEventOp>(std::move(aArgs), 2098 std::move(aCallback)); 2099 break; 2100 case ServiceWorkerOpArgs:: 2101 TServiceWorkerUpdateIsOnContentBlockingAllowListOpArgs: 2102 op = MakeRefPtr<UpdateIsOnContentBlockingAllowListOp>( 2103 std::move(aArgs), std::move(aCallback)); 2104 break; 2105 default: 2106 MOZ_CRASH("Unknown Service Worker operation!"); 2107 return nullptr; 2108 } 2109 2110 return op.forget(); 2111 } 2112 2113 } // namespace mozilla::dom