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