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