Source code

Revision control

Other Tools

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 "nsIHttpChannelInternal.h"
20
#include "nsIHttpHeaderVisitor.h"
21
#include "nsIInputStream.h"
22
#include "nsILoadInfo.h"
23
#include "nsINetworkInterceptController.h"
24
#include "nsIObserverService.h"
25
#include "nsIScriptError.h"
26
#include "nsISupportsImpl.h"
27
#include "nsIURI.h"
28
#include "nsIUploadChannel2.h"
29
#include "nsNetUtil.h"
30
#include "nsProxyRelease.h"
31
#include "nsTArray.h"
32
#include "nsThreadUtils.h"
33
34
#include "ServiceWorkerPrivate.h"
35
#include "mozilla/Assertions.h"
36
#include "mozilla/LoadInfo.h"
37
#include "mozilla/Services.h"
38
#include "mozilla/Telemetry.h"
39
#include "mozilla/UniquePtr.h"
40
#include "mozilla/Unused.h"
41
#include "mozilla/ipc/BackgroundChild.h"
42
#include "mozilla/dom/InternalHeaders.h"
43
#include "mozilla/dom/InternalRequest.h"
44
#include "mozilla/dom/InternalResponse.h"
45
#include "mozilla/dom/PRemoteWorkerControllerChild.h"
46
#include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
47
#include "mozilla/net/NeckoChannelParams.h"
48
49
namespace mozilla {
50
51
using ipc::AutoIPCStream;
52
using ipc::BackgroundChild;
53
using ipc::PBackgroundChild;
54
55
namespace dom {
56
57
namespace {
58
59
bool CSPPermitsResponse(nsILoadInfo* aLoadInfo, InternalResponse* aResponse,
60
const nsACString& aWorkerScriptSpec) {
61
AssertIsOnMainThread();
62
MOZ_ASSERT(aLoadInfo);
63
64
nsCString url = aResponse->GetUnfilteredURL();
65
if (url.IsEmpty()) {
66
// Synthetic response.
67
url = aWorkerScriptSpec;
68
}
69
70
nsCOMPtr<nsIURI> uri;
71
nsresult rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr);
72
if (NS_WARN_IF(NS_FAILED(rv))) {
73
return false;
74
}
75
76
int16_t decision = nsIContentPolicy::ACCEPT;
77
rv = NS_CheckContentLoadPolicy(uri, aLoadInfo, EmptyCString(), &decision);
78
if (NS_WARN_IF(NS_FAILED(rv))) {
79
return false;
80
}
81
82
return decision == nsIContentPolicy::ACCEPT;
83
}
84
85
void AsyncLog(nsIInterceptedChannel* aChannel, const nsACString& aScriptSpec,
86
uint32_t aLineNumber, uint32_t aColumnNumber,
87
const nsACString& aMessageName, nsTArray<nsString>&& aParams) {
88
AssertIsOnMainThread();
89
MOZ_ASSERT(aChannel);
90
91
nsCOMPtr<nsIConsoleReportCollector> reporter =
92
aChannel->GetConsoleReportCollector();
93
94
if (reporter) {
95
// NOTE: is appears that `const nsTArray<nsString>&` is required for
96
// nsIConsoleReportCollector::AddConsoleReport to resolve to the correct
97
// overload.
98
const nsTArray<nsString> params = std::move(aParams);
99
100
reporter->AddConsoleReport(
101
nsIScriptError::errorFlag,
102
NS_LITERAL_CSTRING("Service Worker Interception"),
103
nsContentUtils::eDOM_PROPERTIES, aScriptSpec, aLineNumber,
104
aColumnNumber, aMessageName, params);
105
}
106
}
107
108
class SynthesizeResponseWatcher final : public nsIInterceptedBodyCallback {
109
public:
110
NS_DECL_THREADSAFE_ISUPPORTS
111
112
SynthesizeResponseWatcher(
113
const nsMainThreadPtrHandle<nsIInterceptedChannel>& aInterceptedChannel,
114
const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
115
const bool aIsNonSubresourceRequest,
116
FetchEventRespondWithClosure&& aClosure, nsAString&& aRequestURL)
117
: mInterceptedChannel(aInterceptedChannel),
118
mRegistration(aRegistration),
119
mIsNonSubresourceRequest(aIsNonSubresourceRequest),
120
mClosure(std::move(aClosure)),
121
mRequestURL(std::move(aRequestURL)) {
122
AssertIsOnMainThread();
123
MOZ_ASSERT(mInterceptedChannel);
124
MOZ_ASSERT(mRegistration);
125
}
126
127
NS_IMETHOD
128
BodyComplete(nsresult aRv) override {
129
AssertIsOnMainThread();
130
MOZ_ASSERT(mInterceptedChannel);
131
132
if (NS_WARN_IF(NS_FAILED(aRv))) {
133
AsyncLog(mInterceptedChannel, mClosure.respondWithScriptSpec(),
134
mClosure.respondWithLineNumber(),
135
mClosure.respondWithColumnNumber(),
136
NS_LITERAL_CSTRING("InterceptionFailedWithURL"), {mRequestURL});
137
138
CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
139
140
return NS_OK;
141
}
142
143
nsresult rv = mInterceptedChannel->FinishSynthesizedResponse();
144
145
if (NS_WARN_IF(NS_FAILED(rv))) {
146
CancelInterception(rv);
147
}
148
149
mInterceptedChannel = nullptr;
150
151
return NS_OK;
152
}
153
154
// See FetchEventOpChild::MaybeScheduleRegistrationUpdate() for comments.
155
void CancelInterception(nsresult aStatus) {
156
AssertIsOnMainThread();
157
MOZ_ASSERT(mInterceptedChannel);
158
MOZ_ASSERT(mRegistration);
159
160
mInterceptedChannel->CancelInterception(aStatus);
161
162
if (mIsNonSubresourceRequest) {
163
mRegistration->MaybeScheduleUpdate();
164
} else {
165
mRegistration->MaybeScheduleTimeCheckAndUpdate();
166
}
167
168
mInterceptedChannel = nullptr;
169
mRegistration = nullptr;
170
}
171
172
private:
173
~SynthesizeResponseWatcher() {
174
if (NS_WARN_IF(mInterceptedChannel)) {
175
CancelInterception(NS_ERROR_DOM_ABORT_ERR);
176
}
177
}
178
179
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
180
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
181
const bool mIsNonSubresourceRequest;
182
const FetchEventRespondWithClosure mClosure;
183
const nsString mRequestURL;
184
};
185
186
NS_IMPL_ISUPPORTS(SynthesizeResponseWatcher, nsIInterceptedBodyCallback)
187
188
class HeaderFiller final : public nsIHttpHeaderVisitor {
189
public:
190
NS_DECL_ISUPPORTS
191
192
explicit HeaderFiller(HeadersGuardEnum aGuard)
193
: mInternalHeaders(new InternalHeaders(aGuard)) {
194
MOZ_ASSERT(mInternalHeaders);
195
}
196
197
NS_IMETHOD
198
VisitHeader(const nsACString& aHeader, const nsACString& aValue) override {
199
ErrorResult result;
200
mInternalHeaders->Append(aHeader, aValue, result);
201
202
if (NS_WARN_IF(result.Failed())) {
203
return result.StealNSResult();
204
}
205
206
return NS_OK;
207
}
208
209
RefPtr<InternalHeaders> Extract() {
210
return RefPtr<InternalHeaders>(std::move(mInternalHeaders));
211
}
212
213
private:
214
~HeaderFiller() = default;
215
216
RefPtr<InternalHeaders> mInternalHeaders;
217
};
218
219
NS_IMPL_ISUPPORTS(HeaderFiller, nsIHttpHeaderVisitor)
220
221
nsresult GetIPCInternalRequest(nsIInterceptedChannel* aChannel,
222
IPCInternalRequest* aOutRequest,
223
UniquePtr<AutoIPCStream>& aAutoStream) {
224
AssertIsOnMainThread();
225
226
nsCOMPtr<nsIURI> uri;
227
nsresult rv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
228
NS_ENSURE_SUCCESS(rv, rv);
229
230
nsCOMPtr<nsIURI> uriNoFragment;
231
rv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriNoFragment));
232
NS_ENSURE_SUCCESS(rv, rv);
233
234
nsCOMPtr<nsIChannel> underlyingChannel;
235
rv = aChannel->GetChannel(getter_AddRefs(underlyingChannel));
236
NS_ENSURE_SUCCESS(rv, rv);
237
238
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(underlyingChannel);
239
MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
240
241
nsCOMPtr<nsIHttpChannelInternal> internalChannel =
242
do_QueryInterface(httpChannel);
243
NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
244
245
nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
246
do_QueryInterface(underlyingChannel);
247
248
nsAutoCString spec;
249
rv = uriNoFragment->GetSpec(spec);
250
NS_ENSURE_SUCCESS(rv, rv);
251
252
nsAutoCString fragment;
253
rv = uri->GetRef(fragment);
254
NS_ENSURE_SUCCESS(rv, rv);
255
256
nsAutoCString method;
257
rv = httpChannel->GetRequestMethod(method);
258
NS_ENSURE_SUCCESS(rv, rv);
259
260
// This is safe due to static_asserts in ServiceWorkerManager.cpp
261
uint32_t cacheModeInt;
262
rv = internalChannel->GetFetchCacheMode(&cacheModeInt);
263
MOZ_ASSERT(NS_SUCCEEDED(rv));
264
RequestCache cacheMode = static_cast<RequestCache>(cacheModeInt);
265
266
RequestMode requestMode =
267
InternalRequest::MapChannelToRequestMode(underlyingChannel);
268
269
// This is safe due to static_asserts in ServiceWorkerManager.cpp
270
uint32_t redirectMode;
271
rv = internalChannel->GetRedirectMode(&redirectMode);
272
MOZ_ASSERT(NS_SUCCEEDED(rv));
273
RequestRedirect requestRedirect = static_cast<RequestRedirect>(redirectMode);
274
275
RequestCredentials requestCredentials =
276
InternalRequest::MapChannelToRequestCredentials(underlyingChannel);
277
278
nsAutoString referrer;
279
ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
280
281
nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
282
if (referrerInfo) {
283
referrerPolicy = referrerInfo->ReferrerPolicy();
284
Unused << referrerInfo->GetComputedReferrerSpec(referrer);
285
}
286
287
uint32_t loadFlags;
288
rv = underlyingChannel->GetLoadFlags(&loadFlags);
289
NS_ENSURE_SUCCESS(rv, rv);
290
291
nsCOMPtr<nsILoadInfo> loadInfo;
292
rv = underlyingChannel->GetLoadInfo(getter_AddRefs(loadInfo));
293
NS_ENSURE_SUCCESS(rv, rv);
294
NS_ENSURE_STATE(loadInfo);
295
296
nsContentPolicyType contentPolicyType = loadInfo->InternalContentPolicyType();
297
298
nsAutoString integrity;
299
rv = internalChannel->GetIntegrityMetadata(integrity);
300
NS_ENSURE_SUCCESS(rv, rv);
301
302
RefPtr<HeaderFiller> headerFiller =
303
MakeRefPtr<HeaderFiller>(HeadersGuardEnum::Request);
304
rv = httpChannel->VisitNonDefaultRequestHeaders(headerFiller);
305
NS_ENSURE_SUCCESS(rv, rv);
306
307
RefPtr<InternalHeaders> internalHeaders = headerFiller->Extract();
308
309
ErrorResult result;
310
internalHeaders->SetGuard(HeadersGuardEnum::Immutable, result);
311
if (NS_WARN_IF(result.Failed())) {
312
return result.StealNSResult();
313
}
314
315
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
316
nsCOMPtr<nsIInputStream> uploadStream;
317
int64_t uploadStreamContentLength = -1;
318
if (uploadChannel) {
319
rv = uploadChannel->CloneUploadStream(&uploadStreamContentLength,
320
getter_AddRefs(uploadStream));
321
NS_ENSURE_SUCCESS(rv, rv);
322
}
323
324
RefPtr<InternalRequest> internalRequest = new InternalRequest(
325
spec, fragment, method, internalHeaders.forget(), cacheMode, requestMode,
326
requestRedirect, requestCredentials, referrer, referrerPolicy,
327
contentPolicyType, integrity);
328
internalRequest->SetBody(uploadStream, uploadStreamContentLength);
329
330
nsAutoCString alternativeDataType;
331
if (cacheInfoChannel &&
332
!cacheInfoChannel->PreferredAlternativeDataTypes().IsEmpty()) {
333
// TODO: the internal request probably needs all the preferred types.
334
alternativeDataType.Assign(
335
cacheInfoChannel->PreferredAlternativeDataTypes()[0].type());
336
internalRequest->SetPreferredAlternativeDataType(alternativeDataType);
337
}
338
339
PBackgroundChild* bgChild = BackgroundChild::GetForCurrentThread();
340
341
if (NS_WARN_IF(!bgChild)) {
342
return NS_ERROR_DOM_INVALID_STATE_ERR;
343
}
344
345
internalRequest->ToIPC(aOutRequest, bgChild, aAutoStream);
346
347
return NS_OK;
348
}
349
350
} // anonymous namespace
351
352
/* static */ RefPtr<GenericPromise> FetchEventOpChild::SendFetchEvent(
353
PRemoteWorkerControllerChild* aManager,
354
ServiceWorkerFetchEventOpArgs&& aArgs,
355
nsCOMPtr<nsIInterceptedChannel> aInterceptedChannel,
356
RefPtr<ServiceWorkerRegistrationInfo> aRegistration,
357
RefPtr<KeepAliveToken>&& aKeepAliveToken) {
358
AssertIsOnMainThread();
359
MOZ_ASSERT(aManager);
360
MOZ_ASSERT(aInterceptedChannel);
361
MOZ_ASSERT(aKeepAliveToken);
362
363
FetchEventOpChild* actor = new FetchEventOpChild(
364
std::move(aArgs), std::move(aInterceptedChannel),
365
std::move(aRegistration), std::move(aKeepAliveToken));
366
367
// autoStream will contain a pointer into the IPCInternalRequest passed into
368
// GetIPCInternalRequest, so autoStream shouldn't outlive that
369
// IPCInternalRequest or the containing FetchEventOpChild.
370
auto autoStream = MakeUnique<AutoIPCStream>();
371
372
// const_cast-ing the IPCInternalRequest is okay because this is conceptually
373
// part of initializing actor->mArgs, and `autoStream` needs its
374
// IPCStream (physically part of actor->mArgs.internalRequest()) to be in its
375
// final location in memory, so actor->mArgs must be created before this call.
376
nsresult rv = GetIPCInternalRequest(
377
actor->mInterceptedChannel,
378
const_cast<IPCInternalRequest*>(&actor->mArgs.internalRequest()),
379
autoStream);
380
if (NS_WARN_IF(NS_FAILED(rv))) {
381
// `actor` must be manually delete-d before the actor tree can manage its
382
// lifetime starting with SendPFetchEventOpConstructor.
383
delete actor;
384
return GenericPromise::CreateAndReject(rv, __func__);
385
}
386
387
Unused << aManager->SendPFetchEventOpConstructor(actor, actor->mArgs);
388
autoStream->TakeOptionalValue();
389
390
return actor->mPromiseHolder.Ensure(__func__);
391
}
392
393
FetchEventOpChild::~FetchEventOpChild() {
394
AssertIsOnMainThread();
395
MOZ_ASSERT(mInterceptedChannelHandled);
396
MOZ_DIAGNOSTIC_ASSERT(mPromiseHolder.IsEmpty());
397
}
398
399
FetchEventOpChild::FetchEventOpChild(
400
ServiceWorkerFetchEventOpArgs&& aArgs,
401
nsCOMPtr<nsIInterceptedChannel>&& aInterceptedChannel,
402
RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
403
RefPtr<KeepAliveToken>&& aKeepAliveToken)
404
: mArgs(std::move(aArgs)),
405
mInterceptedChannel(std::move(aInterceptedChannel)),
406
mRegistration(std::move(aRegistration)),
407
mKeepAliveToken(std::move(aKeepAliveToken)) {}
408
409
mozilla::ipc::IPCResult FetchEventOpChild::RecvAsyncLog(
410
const nsCString& aScriptSpec, const uint32_t& aLineNumber,
411
const uint32_t& aColumnNumber, const nsCString& aMessageName,
412
nsTArray<nsString>&& aParams) {
413
AssertIsOnMainThread();
414
MOZ_ASSERT(mInterceptedChannel);
415
416
AsyncLog(mInterceptedChannel, aScriptSpec, aLineNumber, aColumnNumber,
417
aMessageName, std::move(aParams));
418
419
return IPC_OK();
420
}
421
422
mozilla::ipc::IPCResult FetchEventOpChild::RecvRespondWith(
423
IPCFetchEventRespondWithResult&& aResult) {
424
AssertIsOnMainThread();
425
426
switch (aResult.type()) {
427
case IPCFetchEventRespondWithResult::TIPCSynthesizeResponseArgs:
428
SynthesizeResponse(std::move(aResult.get_IPCSynthesizeResponseArgs()));
429
break;
430
case IPCFetchEventRespondWithResult::TResetInterceptionArgs:
431
ResetInterception();
432
break;
433
case IPCFetchEventRespondWithResult::TCancelInterceptionArgs:
434
CancelInterception(aResult.get_CancelInterceptionArgs().status());
435
break;
436
default:
437
MOZ_CRASH("Unknown IPCFetchEventRespondWithResult type!");
438
break;
439
}
440
441
return IPC_OK();
442
}
443
444
mozilla::ipc::IPCResult FetchEventOpChild::Recv__delete__(
445
const ServiceWorkerFetchEventOpResult& aResult) {
446
AssertIsOnMainThread();
447
MOZ_ASSERT(mRegistration);
448
449
if (NS_WARN_IF(!mInterceptedChannelHandled)) {
450
MOZ_ASSERT(NS_FAILED(aResult.rv()));
451
NS_WARNING(
452
"Failed to handle intercepted network request; canceling "
453
"interception!");
454
455
CancelInterception(aResult.rv());
456
}
457
458
mPromiseHolder.ResolveIfExists(true, __func__);
459
460
/**
461
* This corresponds to the "Fire Functional Event" algorithm's step 9:
462
*
463
* "If the time difference in seconds calculated by the current time minus
464
* registration's last update check time is greater than 84600, invoke Soft
465
* Update algorithm with registration."
466
*
467
* TODO: this is probably being called later than it should be; it should be
468
* called ASAP after dispatching the FetchEvent.
469
*/
470
mRegistration->MaybeScheduleTimeCheckAndUpdate();
471
472
return IPC_OK();
473
}
474
475
void FetchEventOpChild::ActorDestroy(ActorDestroyReason) {
476
AssertIsOnMainThread();
477
478
// If `Recv__delete__` was called, it would have resolved the promise already.
479
mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
480
481
if (NS_WARN_IF(!mInterceptedChannelHandled)) {
482
Unused << Recv__delete__(NS_ERROR_DOM_ABORT_ERR);
483
}
484
}
485
486
nsresult FetchEventOpChild::StartSynthesizedResponse(
487
IPCSynthesizeResponseArgs&& aArgs) {
488
AssertIsOnMainThread();
489
MOZ_ASSERT(mInterceptedChannel);
490
MOZ_ASSERT(!mInterceptedChannelHandled);
491
MOZ_ASSERT(mRegistration);
492
493
/**
494
* TODO: moving the IPCInternalResponse won't do anything right now because
495
* there isn't a prefect-forwarding or rvalue-ref-parameter overload of
496
* `InternalResponse::FromIPC().`
497
*/
498
RefPtr<InternalResponse> response =
499
InternalResponse::FromIPC(aArgs.internalResponse());
500
if (NS_WARN_IF(!response)) {
501
return NS_ERROR_FAILURE;
502
}
503
504
nsCOMPtr<nsIChannel> underlyingChannel;
505
nsresult rv =
506
mInterceptedChannel->GetChannel(getter_AddRefs(underlyingChannel));
507
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!underlyingChannel)) {
508
return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
509
}
510
511
nsCOMPtr<nsILoadInfo> loadInfo = underlyingChannel->LoadInfo();
512
if (!CSPPermitsResponse(loadInfo, response, mArgs.workerScriptSpec())) {
513
return NS_ERROR_CONTENT_BLOCKED;
514
}
515
516
MOZ_ASSERT(response->GetChannelInfo().IsInitialized());
517
ChannelInfo channelInfo = response->GetChannelInfo();
518
rv = mInterceptedChannel->SetChannelInfo(&channelInfo);
519
if (NS_WARN_IF(NS_FAILED(rv))) {
520
return NS_ERROR_INTERCEPTION_FAILED;
521
}
522
523
rv = mInterceptedChannel->SynthesizeStatus(
524
response->GetUnfilteredStatus(), response->GetUnfilteredStatusText());
525
if (NS_WARN_IF(NS_FAILED(rv))) {
526
return rv;
527
}
528
529
AutoTArray<InternalHeaders::Entry, 5> entries;
530
response->UnfilteredHeaders()->GetEntries(entries);
531
for (auto& entry : entries) {
532
mInterceptedChannel->SynthesizeHeader(entry.mName, entry.mValue);
533
}
534
535
auto castLoadInfo = static_cast<mozilla::net::LoadInfo*>(loadInfo.get());
536
castLoadInfo->SynthesizeServiceWorkerTainting(response->GetTainting());
537
538
// Get the preferred alternative data type of the outer channel
539
nsAutoCString preferredAltDataType(EmptyCString());
540
nsCOMPtr<nsICacheInfoChannel> outerChannel =
541
do_QueryInterface(underlyingChannel);
542
if (outerChannel &&
543
!outerChannel->PreferredAlternativeDataTypes().IsEmpty()) {
544
preferredAltDataType.Assign(
545
outerChannel->PreferredAlternativeDataTypes()[0].type());
546
}
547
548
nsCOMPtr<nsIInputStream> body;
549
if (preferredAltDataType.Equals(response->GetAlternativeDataType())) {
550
body = response->TakeAlternativeBody();
551
}
552
if (!body) {
553
response->GetUnfilteredBody(getter_AddRefs(body));
554
} else {
555
Telemetry::ScalarAdd(Telemetry::ScalarID::SW_ALTERNATIVE_BODY_USED_COUNT,
556
1);
557
}
558
559
// Propagate the URL to the content if the request mode is not "navigate".
560
// Note that, we only reflect the final URL if the response.redirected is
561
// false. We propagate all the URLs if the response.redirected is true.
562
const IPCInternalRequest& request = mArgs.internalRequest();
563
nsAutoCString responseURL;
564
if (request.requestMode() != RequestMode::Navigate) {
565
responseURL = response->GetUnfilteredURL();
566
567
// Similar to how we apply the request fragment to redirects automatically
568
// we also want to apply it automatically when propagating the response
569
// URL from a service worker interception. Currently response.url strips
570
// the fragment, so this will never conflict with an existing fragment
571
// on the response. In the future we will have to check for a response
572
// fragment and avoid overriding in that case.
573
if (!request.fragment().IsEmpty() && !responseURL.IsEmpty()) {
574
MOZ_ASSERT(!responseURL.Contains('#'));
575
responseURL.AppendLiteral("#");
576
responseURL.Append(request.fragment());
577
}
578
}
579
580
nsMainThreadPtrHandle<nsIInterceptedChannel> interceptedChannel(
581
new nsMainThreadPtrHolder<nsIInterceptedChannel>(
582
"nsIInterceptedChannel", mInterceptedChannel, false));
583
584
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> registration(
585
new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
586
"ServiceWorkerRegistrationInfo", mRegistration, false));
587
588
nsCString requestURL = request.urlList().LastElement();
589
if (!request.fragment().IsEmpty()) {
590
requestURL.AppendLiteral("#");
591
requestURL.Append(request.fragment());
592
}
593
594
RefPtr<SynthesizeResponseWatcher> watcher = new SynthesizeResponseWatcher(
595
interceptedChannel, registration, mArgs.isNonSubresourceRequest(),
596
std::move(aArgs.closure()), NS_ConvertUTF8toUTF16(responseURL));
597
598
rv = mInterceptedChannel->StartSynthesizedResponse(
599
body, watcher, nullptr /* TODO */, responseURL, response->IsRedirected());
600
if (NS_WARN_IF(NS_FAILED(rv))) {
601
return rv;
602
}
603
604
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
605
if (obsService) {
606
obsService->NotifyObservers(underlyingChannel,
607
"service-worker-synthesized-response", nullptr);
608
}
609
610
return rv;
611
}
612
613
void FetchEventOpChild::SynthesizeResponse(IPCSynthesizeResponseArgs&& aArgs) {
614
AssertIsOnMainThread();
615
MOZ_ASSERT(mInterceptedChannel);
616
MOZ_ASSERT(!mInterceptedChannelHandled);
617
618
nsresult rv = StartSynthesizedResponse(std::move(aArgs));
619
620
if (NS_WARN_IF(NS_FAILED(rv))) {
621
NS_WARNING("Failed to synthesize response!");
622
623
mInterceptedChannel->CancelInterception(rv);
624
}
625
626
mInterceptedChannelHandled = true;
627
628
MaybeScheduleRegistrationUpdate();
629
}
630
631
void FetchEventOpChild::ResetInterception() {
632
AssertIsOnMainThread();
633
MOZ_ASSERT(mInterceptedChannel);
634
MOZ_ASSERT(!mInterceptedChannelHandled);
635
636
nsresult rv = mInterceptedChannel->ResetInterception();
637
638
if (NS_WARN_IF(NS_FAILED(rv))) {
639
NS_WARNING("Failed to resume intercepted network request!");
640
641
mInterceptedChannel->CancelInterception(rv);
642
}
643
644
mInterceptedChannelHandled = true;
645
646
MaybeScheduleRegistrationUpdate();
647
}
648
649
void FetchEventOpChild::CancelInterception(nsresult aStatus) {
650
AssertIsOnMainThread();
651
MOZ_ASSERT(mInterceptedChannel);
652
MOZ_ASSERT(!mInterceptedChannelHandled);
653
MOZ_ASSERT(NS_FAILED(aStatus));
654
655
mInterceptedChannel->CancelInterception(aStatus);
656
mInterceptedChannelHandled = true;
657
658
MaybeScheduleRegistrationUpdate();
659
}
660
661
/**
662
* This corresponds to the "Handle Fetch" algorithm's steps 20.3, 21.2, and
663
* 22.2:
664
*
665
* "If request is a non-subresource request, or request is a subresource
666
* request and the time difference in seconds calculated by the current time
667
* minus registration's last update check time is greater than 86400, invoke
668
* Soft Update algorithm with registration."
669
*/
670
void FetchEventOpChild::MaybeScheduleRegistrationUpdate() const {
671
AssertIsOnMainThread();
672
MOZ_ASSERT(mRegistration);
673
MOZ_ASSERT(mInterceptedChannelHandled);
674
675
if (mArgs.isNonSubresourceRequest()) {
676
mRegistration->MaybeScheduleUpdate();
677
} else {
678
mRegistration->MaybeScheduleTimeCheckAndUpdate();
679
}
680
}
681
682
} // namespace dom
683
} // namespace mozilla