FetchEventOpChild.cpp (22265B)
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 "FetchEventOpChild.h" 8 9 #include <utility> 10 11 #include "MainThreadUtils.h" 12 #include "ServiceWorkerPrivate.h" 13 #include "mozilla/Assertions.h" 14 #include "mozilla/LoadInfo.h" 15 #include "mozilla/Services.h" 16 #include "mozilla/StaticPrefs_dom.h" 17 #include "mozilla/dom/FetchService.h" 18 #include "mozilla/dom/InternalHeaders.h" 19 #include "mozilla/dom/InternalResponse.h" 20 #include "mozilla/dom/PRemoteWorkerControllerChild.h" 21 #include "mozilla/dom/RemoteWorkerControllerChild.h" 22 #include "mozilla/dom/ServiceWorkerRegistrationInfo.h" 23 #include "mozilla/ipc/BackgroundChild.h" 24 #include "mozilla/net/NeckoChannelParams.h" 25 #include "nsContentPolicyUtils.h" 26 #include "nsContentUtils.h" 27 #include "nsDebug.h" 28 #include "nsError.h" 29 #include "nsIChannel.h" 30 #include "nsIConsoleReportCollector.h" 31 #include "nsIContentPolicy.h" 32 #include "nsIInputStream.h" 33 #include "nsILoadInfo.h" 34 #include "nsINetworkInterceptController.h" 35 #include "nsIObserverService.h" 36 #include "nsIScriptError.h" 37 #include "nsISupportsImpl.h" 38 #include "nsIURI.h" 39 #include "nsNetUtil.h" 40 #include "nsProxyRelease.h" 41 #include "nsTArray.h" 42 #include "nsThreadUtils.h" 43 44 namespace mozilla::dom { 45 46 namespace { 47 48 bool CSPPermitsResponse(nsILoadInfo* aLoadInfo, 49 SafeRefPtr<InternalResponse> aResponse, 50 const nsACString& aWorkerScriptSpec) { 51 AssertIsOnMainThread(); 52 MOZ_ASSERT(aLoadInfo); 53 54 nsCString url = aResponse->GetUnfilteredURL(); 55 if (url.IsEmpty()) { 56 // Synthetic response. 57 url = aWorkerScriptSpec; 58 } 59 60 nsCOMPtr<nsIURI> uri; 61 nsresult rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr); 62 if (NS_WARN_IF(NS_FAILED(rv))) { 63 return false; 64 } 65 66 int16_t decision = nsIContentPolicy::ACCEPT; 67 rv = NS_CheckContentLoadPolicy(uri, aLoadInfo, &decision); 68 if (NS_WARN_IF(NS_FAILED(rv))) { 69 return false; 70 } 71 72 return decision == nsIContentPolicy::ACCEPT; 73 } 74 75 void AsyncLog(nsIInterceptedChannel* aChannel, const nsACString& aScriptSpec, 76 uint32_t aLineNumber, uint32_t aColumnNumber, 77 const nsACString& aMessageName, nsTArray<nsString>&& aParams) { 78 AssertIsOnMainThread(); 79 MOZ_ASSERT(aChannel); 80 81 nsCOMPtr<nsIConsoleReportCollector> reporter = 82 aChannel->GetConsoleReportCollector(); 83 84 if (reporter) { 85 // NOTE: is appears that `const nsTArray<nsString>&` is required for 86 // nsIConsoleReportCollector::AddConsoleReport to resolve to the correct 87 // overload. 88 const nsTArray<nsString> params = std::move(aParams); 89 90 reporter->AddConsoleReport( 91 nsIScriptError::errorFlag, "Service Worker Interception"_ns, 92 nsContentUtils::eDOM_PROPERTIES, aScriptSpec, aLineNumber, 93 aColumnNumber, aMessageName, params); 94 } 95 } 96 97 class SynthesizeResponseWatcher final : public nsIInterceptedBodyCallback { 98 public: 99 NS_DECL_THREADSAFE_ISUPPORTS 100 101 SynthesizeResponseWatcher( 102 const nsMainThreadPtrHandle<nsIInterceptedChannel>& aInterceptedChannel, 103 const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, 104 const bool aIsNonSubresourceRequest, 105 FetchEventRespondWithClosure&& aClosure, nsAString&& aRequestURL) 106 : mInterceptedChannel(aInterceptedChannel), 107 mRegistration(aRegistration), 108 mIsNonSubresourceRequest(aIsNonSubresourceRequest), 109 mClosure(std::move(aClosure)), 110 mRequestURL(std::move(aRequestURL)) { 111 AssertIsOnMainThread(); 112 MOZ_ASSERT(mInterceptedChannel); 113 MOZ_ASSERT(mRegistration); 114 } 115 116 NS_IMETHOD 117 BodyComplete(nsresult aRv) override { 118 AssertIsOnMainThread(); 119 MOZ_ASSERT(mInterceptedChannel); 120 121 if (NS_WARN_IF(NS_FAILED(aRv))) { 122 AsyncLog(mInterceptedChannel, mClosure.respondWithScriptSpec(), 123 mClosure.respondWithLineNumber(), 124 mClosure.respondWithColumnNumber(), 125 "InterceptionFailedWithURL"_ns, {mRequestURL}); 126 127 CancelInterception(NS_ERROR_INTERCEPTION_FAILED); 128 129 return NS_OK; 130 } 131 132 nsresult rv = mInterceptedChannel->FinishSynthesizedResponse(); 133 134 if (NS_WARN_IF(NS_FAILED(rv))) { 135 CancelInterception(rv); 136 } 137 138 mInterceptedChannel = nullptr; 139 140 return NS_OK; 141 } 142 143 // See FetchEventOpChild::MaybeScheduleRegistrationUpdate() for comments. 144 void CancelInterception(nsresult aStatus) { 145 AssertIsOnMainThread(); 146 MOZ_ASSERT(mInterceptedChannel); 147 MOZ_ASSERT(mRegistration); 148 149 mInterceptedChannel->CancelInterception(aStatus); 150 151 if (mIsNonSubresourceRequest) { 152 mRegistration->MaybeScheduleUpdate(); 153 } else { 154 mRegistration->MaybeScheduleTimeCheckAndUpdate(); 155 } 156 157 mInterceptedChannel = nullptr; 158 mRegistration = nullptr; 159 } 160 161 private: 162 ~SynthesizeResponseWatcher() { 163 if (NS_WARN_IF(mInterceptedChannel)) { 164 CancelInterception(NS_ERROR_DOM_ABORT_ERR); 165 } 166 } 167 168 nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel; 169 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration; 170 const bool mIsNonSubresourceRequest; 171 const FetchEventRespondWithClosure mClosure; 172 const nsString mRequestURL; 173 }; 174 175 NS_IMPL_ISUPPORTS(SynthesizeResponseWatcher, nsIInterceptedBodyCallback) 176 177 } // anonymous namespace 178 179 /* static */ RefPtr<GenericPromise> FetchEventOpChild::SendFetchEvent( 180 PRemoteWorkerControllerChild* aManager, 181 ParentToParentServiceWorkerFetchEventOpArgs&& aArgs, 182 nsCOMPtr<nsIInterceptedChannel> aInterceptedChannel, 183 RefPtr<ServiceWorkerRegistrationInfo> aRegistration, 184 RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises, 185 RefPtr<KeepAliveToken>&& aKeepAliveToken) { 186 AssertIsOnMainThread(); 187 MOZ_ASSERT(aManager); 188 MOZ_ASSERT(aInterceptedChannel); 189 MOZ_ASSERT(aKeepAliveToken); 190 191 FetchEventOpChild* actor = new FetchEventOpChild( 192 std::move(aArgs), std::move(aInterceptedChannel), 193 std::move(aRegistration), std::move(aPreloadResponseReadyPromises), 194 std::move(aKeepAliveToken)); 195 196 actor->mWasSent = true; 197 RefPtr<GenericPromise> promise = actor->mPromiseHolder.Ensure(__func__); 198 (void)aManager->SendPFetchEventOpConstructor(actor, actor->mArgs); 199 // NOTE: actor may have been destroyed 200 return promise; 201 } 202 203 FetchEventOpChild::~FetchEventOpChild() { 204 AssertIsOnMainThread(); 205 MOZ_ASSERT(mInterceptedChannelHandled); 206 MOZ_DIAGNOSTIC_ASSERT(mPromiseHolder.IsEmpty()); 207 } 208 209 FetchEventOpChild::FetchEventOpChild( 210 ParentToParentServiceWorkerFetchEventOpArgs&& aArgs, 211 nsCOMPtr<nsIInterceptedChannel>&& aInterceptedChannel, 212 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration, 213 RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises, 214 RefPtr<KeepAliveToken>&& aKeepAliveToken) 215 : mArgs(std::move(aArgs)), 216 mInterceptedChannel(std::move(aInterceptedChannel)), 217 mRegistration(std::move(aRegistration)), 218 mKeepAliveToken(std::move(aKeepAliveToken)), 219 mPreloadResponseReadyPromises(std::move(aPreloadResponseReadyPromises)) { 220 if (mPreloadResponseReadyPromises) { 221 // This promise should be configured to use synchronous dispatch, so if it's 222 // already resolved when we run this code then the callback will be called 223 // synchronously and pass the preload response with the constructor message. 224 // 225 // Note that it's fine to capture the this pointer in the callbacks because 226 // we disconnect the request in Recv__delete__(). 227 mPreloadResponseReadyPromises->GetResponseAvailablePromise() 228 ->Then( 229 GetCurrentSerialEventTarget(), __func__, 230 [this](FetchServiceResponse&& aResponse) { 231 if (!mWasSent) { 232 // The actor wasn't sent yet, we can still send the preload 233 // response with it. 234 mArgs.preloadResponse() = 235 Some(aResponse->ToParentToParentInternalResponse()); 236 } else { 237 // It's too late to send the preload response with the actor, we 238 // have to send it in a separate message. 239 SendPreloadResponse( 240 aResponse->ToParentToParentInternalResponse()); 241 } 242 mPreloadResponseAvailablePromiseRequestHolder.Complete(); 243 }, 244 [this](const CopyableErrorResult&) { 245 mPreloadResponseAvailablePromiseRequestHolder.Complete(); 246 }) 247 ->Track(mPreloadResponseAvailablePromiseRequestHolder); 248 249 mPreloadResponseReadyPromises->GetResponseTimingPromise() 250 ->Then( 251 GetCurrentSerialEventTarget(), __func__, 252 [this](ResponseTiming&& aTiming) { 253 if (!mWasSent) { 254 // The actor wasn't sent yet, we can still send the preload 255 // response timing with it. 256 mArgs.preloadResponseTiming() = Some(std::move(aTiming)); 257 } else { 258 SendPreloadResponseTiming(aTiming); 259 } 260 mPreloadResponseTimingPromiseRequestHolder.Complete(); 261 }, 262 [this](const CopyableErrorResult&) { 263 mPreloadResponseTimingPromiseRequestHolder.Complete(); 264 }) 265 ->Track(mPreloadResponseTimingPromiseRequestHolder); 266 267 mPreloadResponseReadyPromises->GetResponseEndPromise() 268 ->Then( 269 GetCurrentSerialEventTarget(), __func__, 270 [this](ResponseEndArgs&& aResponse) { 271 if (!mWasSent) { 272 // The actor wasn't sent yet, we can still send the preload 273 // response end args with it. 274 mArgs.preloadResponseEndArgs() = Some(std::move(aResponse)); 275 } else { 276 // It's too late to send the preload response end with the 277 // actor, we have to send it in a separate message. 278 SendPreloadResponseEnd(aResponse); 279 } 280 mPreloadResponseReadyPromises = nullptr; 281 mPreloadResponseEndPromiseRequestHolder.Complete(); 282 }, 283 [this](const CopyableErrorResult&) { 284 mPreloadResponseReadyPromises = nullptr; 285 mPreloadResponseEndPromiseRequestHolder.Complete(); 286 }) 287 ->Track(mPreloadResponseEndPromiseRequestHolder); 288 } 289 } 290 291 mozilla::ipc::IPCResult FetchEventOpChild::RecvAsyncLog( 292 const nsCString& aScriptSpec, const uint32_t& aLineNumber, 293 const uint32_t& aColumnNumber, const nsCString& aMessageName, 294 nsTArray<nsString>&& aParams) { 295 AssertIsOnMainThread(); 296 MOZ_ASSERT(mInterceptedChannel); 297 298 AsyncLog(mInterceptedChannel, aScriptSpec, aLineNumber, aColumnNumber, 299 aMessageName, std::move(aParams)); 300 301 return IPC_OK(); 302 } 303 304 mozilla::ipc::IPCResult FetchEventOpChild::RecvRespondWith( 305 ParentToParentFetchEventRespondWithResult&& aResult) { 306 AssertIsOnMainThread(); 307 308 RefPtr<RemoteWorkerControllerChild> mgr = 309 static_cast<RemoteWorkerControllerChild*>(Manager()); 310 311 mInterceptedChannel->SetRemoteWorkerLaunchStart( 312 mgr->GetRemoteWorkerLaunchStart()); 313 mInterceptedChannel->SetRemoteWorkerLaunchEnd( 314 mgr->GetRemoteWorkerLaunchEnd()); 315 316 switch (aResult.type()) { 317 case ParentToParentFetchEventRespondWithResult:: 318 TParentToParentSynthesizeResponseArgs: 319 mInterceptedChannel->SetFetchHandlerStart( 320 aResult.get_ParentToParentSynthesizeResponseArgs() 321 .timeStamps() 322 .fetchHandlerStart()); 323 mInterceptedChannel->SetFetchHandlerFinish( 324 aResult.get_ParentToParentSynthesizeResponseArgs() 325 .timeStamps() 326 .fetchHandlerFinish()); 327 SynthesizeResponse( 328 std::move(aResult.get_ParentToParentSynthesizeResponseArgs())); 329 break; 330 case ParentToParentFetchEventRespondWithResult::TResetInterceptionArgs: 331 mInterceptedChannel->SetFetchHandlerStart( 332 aResult.get_ResetInterceptionArgs().timeStamps().fetchHandlerStart()); 333 mInterceptedChannel->SetFetchHandlerFinish( 334 aResult.get_ResetInterceptionArgs() 335 .timeStamps() 336 .fetchHandlerFinish()); 337 ResetInterception(false); 338 break; 339 case ParentToParentFetchEventRespondWithResult::TCancelInterceptionArgs: 340 mInterceptedChannel->SetFetchHandlerStart( 341 aResult.get_CancelInterceptionArgs() 342 .timeStamps() 343 .fetchHandlerStart()); 344 mInterceptedChannel->SetFetchHandlerFinish( 345 aResult.get_CancelInterceptionArgs() 346 .timeStamps() 347 .fetchHandlerFinish()); 348 CancelInterception(aResult.get_CancelInterceptionArgs().status()); 349 break; 350 default: 351 MOZ_CRASH("Unknown IPCFetchEventRespondWithResult type!"); 352 break; 353 } 354 355 return IPC_OK(); 356 } 357 358 mozilla::ipc::IPCResult FetchEventOpChild::Recv__delete__( 359 const ServiceWorkerFetchEventOpResult& aResult) { 360 AssertIsOnMainThread(); 361 MOZ_ASSERT(mRegistration); 362 363 if (NS_WARN_IF(!mInterceptedChannelHandled)) { 364 MOZ_ASSERT(NS_FAILED(aResult.rv())); 365 NS_WARNING( 366 "Failed to handle intercepted network request; canceling " 367 "interception!"); 368 369 CancelInterception(aResult.rv()); 370 } 371 372 mPromiseHolder.ResolveIfExists(true, __func__); 373 374 // FetchEvent is completed. 375 // Disconnect preload response related promises and cancel the preload. 376 mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists(); 377 mPreloadResponseTimingPromiseRequestHolder.DisconnectIfExists(); 378 mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists(); 379 if (mPreloadResponseReadyPromises) { 380 RefPtr<FetchService> fetchService = FetchService::GetInstance(); 381 fetchService->CancelFetch(std::move(mPreloadResponseReadyPromises), false); 382 } 383 384 /** 385 * This corresponds to the "Fire Functional Event" algorithm's step 9: 386 * 387 * "If the time difference in seconds calculated by the current time minus 388 * registration's last update check time is greater than 84600, invoke Soft 389 * Update algorithm with registration." 390 * 391 * TODO: this is probably being called later than it should be; it should be 392 * called ASAP after dispatching the FetchEvent. 393 */ 394 mRegistration->MaybeScheduleTimeCheckAndUpdate(); 395 396 return IPC_OK(); 397 } 398 399 void FetchEventOpChild::ActorDestroy(ActorDestroyReason) { 400 AssertIsOnMainThread(); 401 402 // If `Recv__delete__` was called, it would have resolved the promise already. 403 mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__); 404 405 if (NS_WARN_IF(!mInterceptedChannelHandled)) { 406 (void)Recv__delete__(NS_ERROR_DOM_ABORT_ERR); 407 } 408 } 409 410 nsresult FetchEventOpChild::StartSynthesizedResponse( 411 ParentToParentSynthesizeResponseArgs&& aArgs) { 412 AssertIsOnMainThread(); 413 MOZ_ASSERT(mInterceptedChannel); 414 MOZ_ASSERT(!mInterceptedChannelHandled); 415 MOZ_ASSERT(mRegistration); 416 417 /** 418 * TODO: moving the IPCInternalResponse won't do anything right now because 419 * there isn't a prefect-forwarding or rvalue-ref-parameter overload of 420 * `InternalResponse::FromIPC().` 421 */ 422 SafeRefPtr<InternalResponse> response = 423 InternalResponse::FromIPC(aArgs.internalResponse()); 424 if (NS_WARN_IF(!response)) { 425 return NS_ERROR_FAILURE; 426 } 427 428 nsCOMPtr<nsIChannel> underlyingChannel; 429 nsresult rv = 430 mInterceptedChannel->GetChannel(getter_AddRefs(underlyingChannel)); 431 if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!underlyingChannel)) { 432 return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE; 433 } 434 435 nsCOMPtr<nsILoadInfo> loadInfo = underlyingChannel->LoadInfo(); 436 if (!CSPPermitsResponse(loadInfo, response.clonePtr(), 437 mArgs.common().workerScriptSpec())) { 438 return NS_ERROR_CONTENT_BLOCKED; 439 } 440 441 MOZ_ASSERT(response->GetChannelInfo().IsInitialized()); 442 ChannelInfo channelInfo = response->GetChannelInfo(); 443 rv = mInterceptedChannel->SetChannelInfo(&channelInfo); 444 if (NS_WARN_IF(NS_FAILED(rv))) { 445 return NS_ERROR_INTERCEPTION_FAILED; 446 } 447 448 rv = mInterceptedChannel->SynthesizeStatus( 449 response->GetUnfilteredStatus(), response->GetUnfilteredStatusText()); 450 if (NS_WARN_IF(NS_FAILED(rv))) { 451 return rv; 452 } 453 454 AutoTArray<InternalHeaders::Entry, 5> entries; 455 response->UnfilteredHeaders()->GetEntries(entries); 456 for (auto& entry : entries) { 457 mInterceptedChannel->SynthesizeHeader(entry.mName, entry.mValue); 458 } 459 460 auto castLoadInfo = static_cast<mozilla::net::LoadInfo*>(loadInfo.get()); 461 castLoadInfo->SynthesizeServiceWorkerTainting(response->GetTainting()); 462 463 // Get the preferred alternative data type of the outer channel 464 nsAutoCString preferredAltDataType(""_ns); 465 nsCOMPtr<nsICacheInfoChannel> outerChannel = 466 do_QueryInterface(underlyingChannel); 467 if (outerChannel && 468 !outerChannel->PreferredAlternativeDataTypes().IsEmpty()) { 469 preferredAltDataType.Assign( 470 outerChannel->PreferredAlternativeDataTypes()[0].type()); 471 } 472 473 nsCOMPtr<nsIInputStream> body; 474 if (preferredAltDataType.Equals(response->GetAlternativeDataType())) { 475 body = response->TakeAlternativeBody(); 476 } 477 if (!body) { 478 response->GetUnfilteredBody(getter_AddRefs(body)); 479 } 480 481 // Propagate the URL to the content if the request mode is not "navigate". 482 // Note that, we only reflect the final URL if the response.redirected is 483 // false. We propagate all the URLs if the response.redirected is true. 484 const IPCInternalRequest& request = mArgs.common().internalRequest(); 485 nsAutoCString responseURL; 486 if (request.requestMode() != RequestMode::Navigate) { 487 responseURL = response->GetUnfilteredURL(); 488 489 // Similar to how we apply the request fragment to redirects automatically 490 // we also want to apply it automatically when propagating the response 491 // URL from a service worker interception. Currently response.url strips 492 // the fragment, so this will never conflict with an existing fragment 493 // on the response. In the future we will have to check for a response 494 // fragment and avoid overriding in that case. 495 if (!request.fragment().IsEmpty() && !responseURL.IsEmpty()) { 496 MOZ_ASSERT(!responseURL.Contains('#')); 497 responseURL.AppendLiteral("#"); 498 responseURL.Append(request.fragment()); 499 } 500 } 501 502 nsMainThreadPtrHandle<nsIInterceptedChannel> interceptedChannel( 503 new nsMainThreadPtrHolder<nsIInterceptedChannel>( 504 "nsIInterceptedChannel", mInterceptedChannel, false)); 505 506 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> registration( 507 new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>( 508 "ServiceWorkerRegistrationInfo", mRegistration, false)); 509 510 nsCString requestURL = request.urlList().LastElement(); 511 if (!request.fragment().IsEmpty()) { 512 requestURL.AppendLiteral("#"); 513 requestURL.Append(request.fragment()); 514 } 515 516 RefPtr<SynthesizeResponseWatcher> watcher = new SynthesizeResponseWatcher( 517 interceptedChannel, registration, 518 mArgs.common().isNonSubresourceRequest(), std::move(aArgs.closure()), 519 NS_ConvertUTF8toUTF16(responseURL)); 520 521 rv = mInterceptedChannel->StartSynthesizedResponse( 522 body, watcher, nullptr /* TODO */, responseURL, response->IsRedirected()); 523 if (NS_WARN_IF(NS_FAILED(rv))) { 524 return rv; 525 } 526 527 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); 528 if (obsService) { 529 obsService->NotifyObservers(underlyingChannel, 530 "service-worker-synthesized-response", nullptr); 531 } 532 533 return rv; 534 } 535 536 void FetchEventOpChild::SynthesizeResponse( 537 ParentToParentSynthesizeResponseArgs&& aArgs) { 538 AssertIsOnMainThread(); 539 MOZ_ASSERT(mInterceptedChannel); 540 MOZ_ASSERT(!mInterceptedChannelHandled); 541 542 nsresult rv = StartSynthesizedResponse(std::move(aArgs)); 543 544 if (NS_WARN_IF(NS_FAILED(rv))) { 545 NS_WARNING("Failed to synthesize response!"); 546 547 mInterceptedChannel->CancelInterception(rv); 548 } 549 550 mInterceptedChannelHandled = true; 551 552 MaybeScheduleRegistrationUpdate(); 553 } 554 555 void FetchEventOpChild::ResetInterception(bool aBypass) { 556 AssertIsOnMainThread(); 557 MOZ_ASSERT(mInterceptedChannel); 558 MOZ_ASSERT(!mInterceptedChannelHandled); 559 560 nsresult rv = mInterceptedChannel->ResetInterception(aBypass); 561 562 if (NS_WARN_IF(NS_FAILED(rv))) { 563 NS_WARNING("Failed to resume intercepted network request!"); 564 565 mInterceptedChannel->CancelInterception(rv); 566 } 567 568 mInterceptedChannelHandled = true; 569 570 MaybeScheduleRegistrationUpdate(); 571 } 572 573 void FetchEventOpChild::CancelInterception(nsresult aStatus) { 574 AssertIsOnMainThread(); 575 MOZ_ASSERT(mInterceptedChannel); 576 MOZ_ASSERT(!mInterceptedChannelHandled); 577 MOZ_ASSERT(NS_FAILED(aStatus)); 578 579 // Report a navigation fault if this is a navigation (and we have an active 580 // worker, which should be the case in non-shutdown/content-process-crash 581 // situations). 582 RefPtr<ServiceWorkerInfo> mActive = mRegistration->GetActive(); 583 if (mActive && mArgs.common().isNonSubresourceRequest()) { 584 mActive->ReportNavigationFault(); 585 // Additional mitigations such as unregistering the registration are handled 586 // in ServiceWorkerRegistrationInfo::MaybeScheduleUpdate which will be 587 // called by MaybeScheduleRegistrationUpdate which gets called by our call 588 // to ResetInterception. 589 if (StaticPrefs::dom_serviceWorkers_mitigations_bypass_on_fault()) { 590 ResetInterception(true); 591 return; 592 } 593 } 594 595 mInterceptedChannel->CancelInterception(aStatus); 596 mInterceptedChannelHandled = true; 597 598 MaybeScheduleRegistrationUpdate(); 599 } 600 601 /** 602 * This corresponds to the "Handle Fetch" algorithm's steps 20.3, 21.2, and 603 * 22.2: 604 * 605 * "If request is a non-subresource request, or request is a subresource 606 * request and the time difference in seconds calculated by the current time 607 * minus registration's last update check time is greater than 86400, invoke 608 * Soft Update algorithm with registration." 609 */ 610 void FetchEventOpChild::MaybeScheduleRegistrationUpdate() const { 611 AssertIsOnMainThread(); 612 MOZ_ASSERT(mRegistration); 613 MOZ_ASSERT(mInterceptedChannelHandled); 614 615 if (mArgs.common().isNonSubresourceRequest()) { 616 mRegistration->MaybeScheduleUpdate(); 617 } else { 618 mRegistration->MaybeScheduleTimeCheckAndUpdate(); 619 } 620 } 621 622 } // namespace mozilla::dom