Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set sw=2 ts=8 et 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 "InterceptedHttpChannel.h"
8
#include "nsContentSecurityManager.h"
9
#include "nsEscape.h"
10
#include "mozilla/dom/PerformanceStorage.h"
11
12
namespace mozilla {
13
namespace net {
14
15
NS_IMPL_ISUPPORTS_INHERITED(InterceptedHttpChannel, HttpBaseChannel,
16
nsIInterceptedChannel, nsICacheInfoChannel,
17
nsIAsyncVerifyRedirectCallback, nsIRequestObserver,
18
nsIStreamListener,
19
nsIChannelWithDivertableParentListener,
20
nsIThreadRetargetableRequest,
21
nsIThreadRetargetableStreamListener)
22
23
InterceptedHttpChannel::InterceptedHttpChannel(
24
PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
25
const TimeStamp& aAsyncOpenTimestamp)
26
: HttpAsyncAborter<InterceptedHttpChannel>(this),
27
mProgress(0),
28
mProgressReported(0),
29
mSynthesizedStreamLength(-1),
30
mResumeStartPos(0),
31
mSynthesizedOrReset(Invalid),
32
mCallingStatusAndProgress(false),
33
mDiverting(false) {
34
// Pre-set the creation and AsyncOpen times based on the original channel
35
// we are intercepting. We don't want our extra internal redirect to mask
36
// any time spent processing the channel.
37
mChannelCreationTime = aCreationTime;
38
mChannelCreationTimestamp = aCreationTimestamp;
39
mAsyncOpenTime = aAsyncOpenTimestamp;
40
}
41
42
void InterceptedHttpChannel::ReleaseListeners() {
43
if (mLoadGroup) {
44
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
45
}
46
HttpBaseChannel::ReleaseListeners();
47
mSynthesizedResponseHead.reset();
48
mRedirectChannel = nullptr;
49
mBodyReader = nullptr;
50
mReleaseHandle = nullptr;
51
mProgressSink = nullptr;
52
mBodyCallback = nullptr;
53
mPump = nullptr;
54
mParentChannel = nullptr;
55
56
MOZ_DIAGNOSTIC_ASSERT(!mIsPending);
57
}
58
59
nsresult InterceptedHttpChannel::SetupReplacementChannel(
60
nsIURI* aURI, nsIChannel* aChannel, bool aPreserveMethod,
61
uint32_t aRedirectFlags) {
62
nsresult rv = HttpBaseChannel::SetupReplacementChannel(
63
aURI, aChannel, aPreserveMethod, aRedirectFlags);
64
if (NS_FAILED(rv)) {
65
return rv;
66
}
67
68
rv = CheckRedirectLimit(aRedirectFlags);
69
NS_ENSURE_SUCCESS(rv, rv);
70
71
// While we can't resume an synthetic response, we can still propagate
72
// the resume params across redirects for other channels to handle.
73
if (mResumeStartPos > 0) {
74
nsCOMPtr<nsIResumableChannel> resumable = do_QueryInterface(aChannel);
75
if (!resumable) {
76
return NS_ERROR_NOT_RESUMABLE;
77
}
78
79
resumable->ResumeAt(mResumeStartPos, mResumeEntityId);
80
}
81
82
return NS_OK;
83
}
84
85
void InterceptedHttpChannel::AsyncOpenInternal() {
86
// If an error occurs in this file we must ensure mListener callbacks are
87
// invoked in some way. We either Cancel() or ResetInterception below
88
// depending on which path we take.
89
nsresult rv = NS_OK;
90
91
// We should have pre-set the AsyncOpen time based on the original channel if
92
// timings are enabled.
93
if (mTimingEnabled) {
94
MOZ_DIAGNOSTIC_ASSERT(!mAsyncOpenTime.IsNull());
95
}
96
97
mIsPending = true;
98
mResponseCouldBeSynthesized = true;
99
100
if (mLoadGroup) {
101
mLoadGroup->AddRequest(this, nullptr);
102
}
103
104
// If we already have a synthesized body then we are pre-synthesized.
105
// This can happen for two reasons:
106
// 1. We have a pre-synthesized redirect in e10s mode. In this case
107
// we should follow the redirect.
108
// 2. We are handling a "fake" redirect for an opaque response. Here
109
// we should just process the synthetic body.
110
if (mBodyReader) {
111
// If we fail in this path, then cancel the channel. We don't want
112
// to ResetInterception() after a synthetic result has already been
113
// produced by the ServiceWorker.
114
auto autoCancel = MakeScopeExit([&] {
115
if (NS_FAILED(rv)) {
116
Cancel(rv);
117
}
118
});
119
120
if (ShouldRedirect()) {
121
rv = FollowSyntheticRedirect();
122
return;
123
}
124
125
rv = StartPump();
126
return;
127
}
128
129
// If we fail the initial interception, then attempt to ResetInterception
130
// to fall back to network. We only cancel if the reset fails.
131
auto autoReset = MakeScopeExit([&] {
132
if (NS_FAILED(rv)) {
133
rv = ResetInterception();
134
if (NS_WARN_IF(NS_FAILED(rv))) {
135
Cancel(rv);
136
}
137
}
138
});
139
140
// Otherwise we need to trigger a FetchEvent in a ServiceWorker.
141
nsCOMPtr<nsINetworkInterceptController> controller;
142
GetCallback(controller);
143
144
if (NS_WARN_IF(!controller)) {
145
rv = NS_ERROR_DOM_INVALID_STATE_ERR;
146
return;
147
}
148
149
rv = controller->ChannelIntercepted(this);
150
NS_ENSURE_SUCCESS_VOID(rv);
151
}
152
153
bool InterceptedHttpChannel::ShouldRedirect() const {
154
// Determine if the synthetic response requires us to perform a real redirect.
155
return nsHttpChannel::WillRedirect(mResponseHead) &&
156
!mLoadInfo->GetDontFollowRedirects();
157
}
158
159
nsresult InterceptedHttpChannel::FollowSyntheticRedirect() {
160
// Perform a real redirect based on the synthetic response.
161
162
nsCOMPtr<nsIIOService> ioService;
163
nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
164
NS_ENSURE_SUCCESS(rv, rv);
165
166
nsAutoCString location;
167
rv = mResponseHead->GetHeader(nsHttp::Location, location);
168
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
169
170
// make sure non-ASCII characters in the location header are escaped.
171
nsAutoCString locationBuf;
172
if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
173
locationBuf)) {
174
location = locationBuf;
175
}
176
177
nsCOMPtr<nsIURI> redirectURI;
178
rv = ioService->NewURI(nsDependentCString(location.get()), nullptr, mURI,
179
getter_AddRefs(redirectURI));
180
NS_ENSURE_SUCCESS(rv, NS_ERROR_CORRUPTED_CONTENT);
181
182
uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
183
if (nsHttp::IsPermanentRedirect(mResponseHead->Status())) {
184
redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
185
}
186
187
PropagateReferenceIfNeeded(mURI, redirectURI);
188
189
bool rewriteToGET = ShouldRewriteRedirectToGET(mResponseHead->Status(),
190
mRequestHead.ParsedMethod());
191
192
nsCOMPtr<nsIChannel> newChannel;
193
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
194
CloneLoadInfoForRedirect(redirectURI, redirectFlags);
195
rv = NS_NewChannelInternal(getter_AddRefs(newChannel), redirectURI,
196
redirectLoadInfo,
197
nullptr, // PerformanceStorage
198
nullptr, // aLoadGroup
199
nullptr, // aCallbacks
200
mLoadFlags, ioService);
201
NS_ENSURE_SUCCESS(rv, rv);
202
203
rv = SetupReplacementChannel(redirectURI, newChannel, !rewriteToGET,
204
redirectFlags);
205
NS_ENSURE_SUCCESS(rv, rv);
206
207
mRedirectChannel = newChannel.forget();
208
209
rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel,
210
redirectFlags);
211
212
if (NS_WARN_IF(NS_FAILED(rv))) {
213
OnRedirectVerifyCallback(rv);
214
}
215
216
return rv;
217
}
218
219
nsresult InterceptedHttpChannel::RedirectForResponseURL(
220
nsIURI* aResponseURI, bool aResponseRedirected) {
221
// Perform a service worker redirect to another InterceptedHttpChannel using
222
// the given response URL. It allows content to see the final URL where
223
// appropriate and also helps us enforce cross-origin restrictions. The
224
// resulting channel will then process the synthetic response as normal. This
225
// extra redirect is performed so that listeners treat the result as unsafe
226
// cross-origin data.
227
228
nsresult rv = NS_OK;
229
230
// We want to pass ownership of the body callback to the new synthesized
231
// channel. We need to hold a reference to the callbacks on the stack
232
// as well, though, so we can call them if a failure occurs.
233
nsCOMPtr<nsIInterceptedBodyCallback> bodyCallback = mBodyCallback.forget();
234
235
RefPtr<InterceptedHttpChannel> newChannel = CreateForSynthesis(
236
mResponseHead, mBodyReader, bodyCallback, mChannelCreationTime,
237
mChannelCreationTimestamp, mAsyncOpenTime);
238
239
// If the response has been redirected, propagate all the URLs to content.
240
// Thus, the exact value of the redirect flag does not matter as long as it's
241
// not REDIRECT_INTERNAL.
242
uint32_t flags = aResponseRedirected ? nsIChannelEventSink::REDIRECT_TEMPORARY
243
: nsIChannelEventSink::REDIRECT_INTERNAL;
244
245
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
246
CloneLoadInfoForRedirect(aResponseURI, flags);
247
248
nsContentPolicyType contentPolicyType =
249
redirectLoadInfo ? redirectLoadInfo->GetExternalContentPolicyType()
250
: nsIContentPolicy::TYPE_OTHER;
251
252
rv = newChannel->Init(
253
aResponseURI, mCaps, static_cast<nsProxyInfo*>(mProxyInfo.get()),
254
mProxyResolveFlags, mProxyURI, mChannelId, contentPolicyType);
255
256
newChannel->SetLoadInfo(redirectLoadInfo);
257
NS_ENSURE_SUCCESS(rv, rv);
258
259
// Normally we don't propagate the LoadInfo's service worker tainting
260
// synthesis flag on redirect. A real redirect normally will want to allow
261
// normal tainting to proceed from its starting taint. For this particular
262
// redirect, though, we are performing a redirect to communicate the URL of
263
// the service worker synthetic response itself. This redirect still
264
// represents the synthetic response, so we must preserve the flag.
265
if (redirectLoadInfo && mLoadInfo &&
266
mLoadInfo->GetServiceWorkerTaintingSynthesized()) {
267
redirectLoadInfo->SynthesizeServiceWorkerTainting(mLoadInfo->GetTainting());
268
}
269
270
rv = SetupReplacementChannel(aResponseURI, newChannel, true, flags);
271
NS_ENSURE_SUCCESS(rv, rv);
272
273
mRedirectChannel = newChannel;
274
275
MOZ_ASSERT(mBodyReader);
276
MOZ_ASSERT(!mApplyConversion);
277
newChannel->SetApplyConversion(false);
278
279
rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags);
280
281
if (NS_FAILED(rv)) {
282
// Make sure to call the body callback since we took ownership
283
// above. Neither the new channel or our standard
284
// OnRedirectVerifyCallback() code will invoke the callback. Do it here.
285
bodyCallback->BodyComplete(rv);
286
287
OnRedirectVerifyCallback(rv);
288
}
289
290
return rv;
291
}
292
293
nsresult InterceptedHttpChannel::StartPump() {
294
MOZ_DIAGNOSTIC_ASSERT(!mPump);
295
MOZ_DIAGNOSTIC_ASSERT(mBodyReader);
296
297
// We don't support resuming an intercepted channel. We can't guarantee the
298
// ServiceWorker will always return the same data and we can't rely on the
299
// http cache code to detect changes. For now, just force the channel to
300
// NS_ERROR_NOT_RESUMABLE which should cause the front-end to recreate the
301
// channel without calling ResumeAt().
302
//
303
// It would also be possible to convert this information to a range request,
304
// but its unclear if we should do that for ServiceWorker FetchEvents. See:
305
//
307
if (mResumeStartPos > 0) {
308
return NS_ERROR_NOT_RESUMABLE;
309
}
310
311
// For progress we trust the content-length for the "maximum" size.
312
// We can't determine the full size from the stream itself since
313
// we may only receive the data incrementally. We can't trust
314
// Available() here.
315
// TODO: We could implement an nsIFixedLengthInputStream interface and
316
// QI to it here. This would let us determine the total length
317
// for streams that support it. See bug 1388774.
318
Unused << GetContentLength(&mSynthesizedStreamLength);
319
320
nsresult rv =
321
nsInputStreamPump::Create(getter_AddRefs(mPump), mBodyReader, 0, 0, true);
322
NS_ENSURE_SUCCESS(rv, rv);
323
324
rv = mPump->AsyncRead(this, nullptr);
325
NS_ENSURE_SUCCESS(rv, rv);
326
327
uint32_t suspendCount = mSuspendCount;
328
while (suspendCount--) {
329
mPump->Suspend();
330
}
331
332
MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
333
334
return rv;
335
}
336
337
nsresult InterceptedHttpChannel::OpenRedirectChannel() {
338
nsresult rv = NS_OK;
339
340
if (NS_FAILED(mStatus)) {
341
return mStatus;
342
}
343
344
if (!mRedirectChannel) {
345
return NS_ERROR_DOM_ABORT_ERR;
346
}
347
348
// Make sure to do this after we received redirect veto answer,
349
// i.e. after all sinks had been notified
350
mRedirectChannel->SetOriginalURI(mOriginalURI);
351
352
// open new channel
353
rv = mRedirectChannel->AsyncOpen(mListener);
354
NS_ENSURE_SUCCESS(rv, rv);
355
356
mStatus = NS_BINDING_REDIRECTED;
357
358
return rv;
359
}
360
361
void InterceptedHttpChannel::MaybeCallStatusAndProgress() {
362
// OnStatus() and OnProgress() must only be called on the main thread. If
363
// we are on a separate thread, then we maybe need to schedule a runnable
364
// to call them asynchronousnly.
365
if (!NS_IsMainThread()) {
366
// Check to see if we are already trying to call OnStatus/OnProgress
367
// asynchronously. If we are, then don't queue up another runnable.
368
// We don't want to flood the main thread.
369
if (mCallingStatusAndProgress) {
370
return;
371
}
372
mCallingStatusAndProgress = true;
373
374
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
375
"InterceptedHttpChannel::MaybeCallStatusAndProgress", this,
376
&InterceptedHttpChannel::MaybeCallStatusAndProgress);
377
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
378
379
return;
380
}
381
382
MOZ_ASSERT(NS_IsMainThread());
383
384
// We are about to capture out progress position. Clear the flag we use
385
// to de-duplicate progress report runnables. We want any further progress
386
// updates to trigger another runnable. We do this before capture the
387
// progress value since we're using atomics and not a mutex lock.
388
mCallingStatusAndProgress = false;
389
390
// Capture the current status from our atomic count.
391
int64_t progress = mProgress;
392
393
MOZ_DIAGNOSTIC_ASSERT(progress >= mProgressReported);
394
395
// Do nothing if we've already made the calls for this amount of progress
396
// or if the channel is not configured for these calls. Note, the check
397
// for mProgressSink here means we will not fire any spurious late calls
398
// after ReleaseListeners() is executed.
399
if (progress <= mProgressReported || mCanceled || !mProgressSink ||
400
(mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
401
return;
402
}
403
404
// Capture the host name on the first set of calls to avoid doing this
405
// string processing repeatedly.
406
if (mProgressReported == 0) {
407
nsAutoCString host;
408
MOZ_ALWAYS_SUCCEEDS(mURI->GetHost(host));
409
CopyUTF8toUTF16(host, mStatusHost);
410
}
411
412
mProgressSink->OnStatus(this, nullptr, NS_NET_STATUS_READING,
413
mStatusHost.get());
414
415
mProgressSink->OnProgress(this, nullptr, progress, mSynthesizedStreamLength);
416
417
mProgressReported = progress;
418
}
419
420
void InterceptedHttpChannel::MaybeCallBodyCallback() {
421
nsCOMPtr<nsIInterceptedBodyCallback> callback = mBodyCallback.forget();
422
if (callback) {
423
callback->BodyComplete(mStatus);
424
}
425
}
426
427
// static
428
already_AddRefed<InterceptedHttpChannel>
429
InterceptedHttpChannel::CreateForInterception(
430
PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
431
const TimeStamp& aAsyncOpenTimestamp) {
432
// Create an InterceptedHttpChannel that will trigger a FetchEvent
433
// in a ServiceWorker when opened.
434
RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
435
aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
436
437
return ref.forget();
438
}
439
440
// static
441
already_AddRefed<InterceptedHttpChannel>
442
InterceptedHttpChannel::CreateForSynthesis(
443
const nsHttpResponseHead* aHead, nsIInputStream* aBody,
444
nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
445
const TimeStamp& aCreationTimestamp, const TimeStamp& aAsyncOpenTimestamp) {
446
MOZ_DIAGNOSTIC_ASSERT(aHead);
447
MOZ_DIAGNOSTIC_ASSERT(aBody);
448
449
// Create an InterceptedHttpChannel that already has a synthesized response.
450
// The synthetic response will be processed when opened. A FetchEvent
451
// will not be triggered.
452
RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
453
aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
454
455
ref->mResponseHead = new nsHttpResponseHead(*aHead);
456
ref->mBodyReader = aBody;
457
ref->mBodyCallback = aBodyCallback;
458
459
return ref.forget();
460
}
461
462
NS_IMETHODIMP
463
InterceptedHttpChannel::Cancel(nsresult aStatus) {
464
// Note: This class has been designed to send all error results through
465
// Cancel(). Don't add calls directly to AsyncAbort() or
466
// DoNotifyListener(). Instead call Cancel().
467
468
if (mCanceled) {
469
return NS_OK;
470
}
471
mCanceled = true;
472
473
MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(aStatus));
474
if (NS_SUCCEEDED(mStatus)) {
475
mStatus = aStatus;
476
}
477
478
// Everything is suspended during diversion until it completes. Since the
479
// intercepted channel could be a long-running stream, we need to request that
480
// cancellation be triggered in the child, completing the diversion and
481
// allowing cancellation to run to completion.
482
if (mDiverting) {
483
Unused << mParentChannel->CancelDiversion();
484
// (We want the pump to be canceled as well, so don't directly return.)
485
}
486
487
if (mPump) {
488
return mPump->Cancel(mStatus);
489
}
490
491
return AsyncAbort(mStatus);
492
}
493
494
NS_IMETHODIMP
495
InterceptedHttpChannel::Suspend(void) {
496
nsresult rv = SuspendInternal();
497
498
nsresult rvParentChannel = NS_OK;
499
if (mParentChannel) {
500
rvParentChannel = mParentChannel->SuspendMessageDiversion();
501
}
502
503
return NS_FAILED(rv) ? rv : rvParentChannel;
504
}
505
506
NS_IMETHODIMP
507
InterceptedHttpChannel::Resume(void) {
508
nsresult rv = ResumeInternal();
509
510
nsresult rvParentChannel = NS_OK;
511
if (mParentChannel) {
512
rvParentChannel = mParentChannel->ResumeMessageDiversion();
513
}
514
515
return NS_FAILED(rv) ? rv : rvParentChannel;
516
}
517
518
NS_IMETHODIMP
519
InterceptedHttpChannel::GetSecurityInfo(nsISupports** aSecurityInfo) {
520
nsCOMPtr<nsISupports> ref(mSecurityInfo);
521
ref.forget(aSecurityInfo);
522
return NS_OK;
523
}
524
525
NS_IMETHODIMP
526
InterceptedHttpChannel::AsyncOpen(nsIStreamListener* aListener) {
527
nsCOMPtr<nsIStreamListener> listener(aListener);
528
nsresult rv =
529
nsContentSecurityManager::doContentSecurityCheck(this, listener);
530
if (NS_WARN_IF(NS_FAILED(rv))) {
531
Cancel(rv);
532
return rv;
533
}
534
if (mCanceled) {
535
return mStatus;
536
}
537
538
// After this point we should try to return NS_OK and notify the listener
539
// of the result.
540
mListener = aListener;
541
542
AsyncOpenInternal();
543
544
return NS_OK;
545
}
546
547
NS_IMETHODIMP
548
InterceptedHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage,
549
const nsACString& aCategory) {
550
// Synthetic responses should not trigger CORS blocking.
551
return NS_ERROR_NOT_IMPLEMENTED;
552
}
553
554
NS_IMETHODIMP
555
InterceptedHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
556
bool aWarning,
557
const nsAString& aURL,
558
const nsAString& aContentType) {
559
return NS_ERROR_NOT_IMPLEMENTED;
560
}
561
562
NS_IMETHODIMP
563
InterceptedHttpChannel::SetupFallbackChannel(const char* aFallbackKey) {
564
// AppCache should not be used with service worker intercepted channels.
565
// This should never be called.
566
return NS_ERROR_NOT_IMPLEMENTED;
567
}
568
569
NS_IMETHODIMP
570
InterceptedHttpChannel::SetPriority(int32_t aPriority) {
571
mPriority = clamped<int32_t>(aPriority, INT16_MIN, INT16_MAX);
572
return NS_OK;
573
}
574
575
NS_IMETHODIMP
576
InterceptedHttpChannel::SetClassFlags(uint32_t aClassFlags) {
577
mClassOfService = aClassFlags;
578
return NS_OK;
579
}
580
581
NS_IMETHODIMP
582
InterceptedHttpChannel::ClearClassFlags(uint32_t aClassFlags) {
583
mClassOfService &= ~aClassFlags;
584
return NS_OK;
585
}
586
587
NS_IMETHODIMP
588
InterceptedHttpChannel::AddClassFlags(uint32_t aClassFlags) {
589
mClassOfService |= aClassFlags;
590
return NS_OK;
591
}
592
593
NS_IMETHODIMP
594
InterceptedHttpChannel::ResumeAt(uint64_t aStartPos,
595
const nsACString& aEntityId) {
596
// We don't support resuming synthesized responses, but we do track this
597
// information so it can be passed on to the resulting nsHttpChannel if
598
// ResetInterception is called.
599
mResumeStartPos = aStartPos;
600
mResumeEntityId = aEntityId;
601
return NS_OK;
602
}
603
604
void InterceptedHttpChannel::DoNotifyListenerCleanup() {
605
// Prefer to cleanup in ReleaseListeners() as it seems to be called
606
// more consistently in necko.
607
}
608
609
void InterceptedHttpChannel::DoAsyncAbort(nsresult aStatus) {
610
Unused << AsyncAbort(aStatus);
611
}
612
613
NS_IMETHODIMP
614
InterceptedHttpChannel::ResetInterception(void) {
615
if (mCanceled) {
616
return mStatus;
617
}
618
619
uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
620
621
nsCOMPtr<nsIChannel> newChannel;
622
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
623
CloneLoadInfoForRedirect(mURI, flags);
624
nsresult rv =
625
NS_NewChannelInternal(getter_AddRefs(newChannel), mURI, redirectLoadInfo,
626
nullptr, // PerformanceStorage
627
nullptr, // aLoadGroup
628
nullptr, // aCallbacks
629
mLoadFlags);
630
NS_ENSURE_SUCCESS(rv, rv);
631
632
rv = SetupReplacementChannel(mURI, newChannel, true, flags);
633
NS_ENSURE_SUCCESS(rv, rv);
634
635
nsCOMPtr<nsITimedChannel> newTimedChannel = do_QueryInterface(newChannel);
636
if (newTimedChannel) {
637
if (!mAsyncOpenTime.IsNull()) {
638
newTimedChannel->SetAsyncOpen(mAsyncOpenTime);
639
}
640
if (!mChannelCreationTimestamp.IsNull()) {
641
newTimedChannel->SetChannelCreation(mChannelCreationTimestamp);
642
}
643
}
644
645
if (mRedirectMode != nsIHttpChannelInternal::REDIRECT_MODE_MANUAL) {
646
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
647
rv = newChannel->GetLoadFlags(&loadFlags);
648
NS_ENSURE_SUCCESS(rv, rv);
649
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
650
rv = newChannel->SetLoadFlags(loadFlags);
651
NS_ENSURE_SUCCESS(rv, rv);
652
}
653
654
mRedirectChannel = newChannel.forget();
655
656
rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags);
657
658
if (NS_FAILED(rv)) {
659
OnRedirectVerifyCallback(rv);
660
}
661
662
return rv;
663
}
664
665
NS_IMETHODIMP
666
InterceptedHttpChannel::SynthesizeStatus(uint16_t aStatus,
667
const nsACString& aReason) {
668
if (mCanceled) {
669
return mStatus;
670
}
671
672
if (!mSynthesizedResponseHead) {
673
mSynthesizedResponseHead.reset(new nsHttpResponseHead());
674
}
675
676
nsAutoCString statusLine;
677
statusLine.AppendLiteral("HTTP/1.1 ");
678
statusLine.AppendInt(aStatus);
679
statusLine.AppendLiteral(" ");
680
statusLine.Append(aReason);
681
682
mSynthesizedResponseHead->ParseStatusLine(statusLine);
683
return NS_OK;
684
}
685
686
NS_IMETHODIMP
687
InterceptedHttpChannel::SynthesizeHeader(const nsACString& aName,
688
const nsACString& aValue) {
689
if (mCanceled) {
690
return mStatus;
691
}
692
693
if (!mSynthesizedResponseHead) {
694
mSynthesizedResponseHead.reset(new nsHttpResponseHead());
695
}
696
697
nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
698
// Overwrite any existing header.
699
nsresult rv = mSynthesizedResponseHead->ParseHeaderLine(header);
700
NS_ENSURE_SUCCESS(rv, rv);
701
return NS_OK;
702
}
703
704
NS_IMETHODIMP
705
InterceptedHttpChannel::StartSynthesizedResponse(
706
nsIInputStream* aBody, nsIInterceptedBodyCallback* aBodyCallback,
707
nsICacheInfoChannel* aSynthesizedCacheInfo, const nsACString& aFinalURLSpec,
708
bool aResponseRedirected) {
709
nsresult rv = NS_OK;
710
711
auto autoCleanup = MakeScopeExit([&] {
712
// Auto-cancel on failure. Do this first to get mStatus set, if necessary.
713
if (NS_FAILED(rv)) {
714
Cancel(rv);
715
}
716
717
// If we early exit before taking ownership of the body, then automatically
718
// invoke the callback. This could be due to an error or because we're not
719
// going to consume it due to a redirect, etc.
720
if (aBodyCallback) {
721
aBodyCallback->BodyComplete(mStatus);
722
}
723
});
724
725
if (NS_FAILED(mStatus)) {
726
// Return NS_OK. The channel should fire callbacks with an error code
727
// if it was cancelled before this point.
728
return NS_OK;
729
}
730
731
// Take ownership of the body callbacks If a failure occurs we will
732
// automatically Cancel() the channel. This will then invoke OnStopRequest()
733
// which will invoke the correct callback. In the case of an opaque response
734
// redirect we pass ownership of the callback to the new channel.
735
mBodyCallback = aBodyCallback;
736
aBodyCallback = nullptr;
737
738
mSynthesizedCacheInfo = aSynthesizedCacheInfo;
739
740
if (!mSynthesizedResponseHead) {
741
mSynthesizedResponseHead.reset(new nsHttpResponseHead());
742
}
743
744
mResponseHead = mSynthesizedResponseHead.release();
745
746
if (ShouldRedirect()) {
747
rv = FollowSyntheticRedirect();
748
NS_ENSURE_SUCCESS(rv, rv);
749
750
return NS_OK;
751
}
752
753
// Intercepted responses should already be decoded.
754
SetApplyConversion(false);
755
756
// Errors and redirects may not have a body. Synthesize an empty string
757
// stream here so later code can be simpler.
758
mBodyReader = aBody;
759
if (!mBodyReader) {
760
rv = NS_NewCStringInputStream(getter_AddRefs(mBodyReader), EmptyCString());
761
NS_ENSURE_SUCCESS(rv, rv);
762
}
763
764
nsCOMPtr<nsIURI> responseURI;
765
if (!aFinalURLSpec.IsEmpty()) {
766
rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec);
767
NS_ENSURE_SUCCESS(rv, rv);
768
} else {
769
responseURI = mURI;
770
}
771
772
bool equal = false;
773
Unused << mURI->Equals(responseURI, &equal);
774
if (!equal) {
775
rv = RedirectForResponseURL(responseURI, aResponseRedirected);
776
NS_ENSURE_SUCCESS(rv, rv);
777
778
return NS_OK;
779
}
780
781
rv = StartPump();
782
NS_ENSURE_SUCCESS(rv, rv);
783
784
return NS_OK;
785
}
786
787
NS_IMETHODIMP
788
InterceptedHttpChannel::FinishSynthesizedResponse() {
789
if (mCanceled) {
790
// Return NS_OK. The channel should fire callbacks with an error code
791
// if it was cancelled before this point.
792
return NS_OK;
793
}
794
795
// TODO: Remove this API after interception moves to the parent process in
796
// e10s mode.
797
798
return NS_OK;
799
}
800
801
NS_IMETHODIMP
802
InterceptedHttpChannel::CancelInterception(nsresult aStatus) {
803
return Cancel(aStatus);
804
}
805
806
NS_IMETHODIMP
807
InterceptedHttpChannel::GetChannel(nsIChannel** aChannel) {
808
nsCOMPtr<nsIChannel> ref(this);
809
ref.forget(aChannel);
810
return NS_OK;
811
}
812
813
NS_IMETHODIMP
814
InterceptedHttpChannel::GetSecureUpgradedChannelURI(
815
nsIURI** aSecureUpgradedChannelURI) {
816
nsCOMPtr<nsIURI> ref(mURI);
817
ref.forget(aSecureUpgradedChannelURI);
818
return NS_OK;
819
}
820
821
NS_IMETHODIMP
822
InterceptedHttpChannel::SetChannelInfo(
823
mozilla::dom::ChannelInfo* aChannelInfo) {
824
return aChannelInfo->ResurrectInfoOnChannel(this);
825
}
826
827
NS_IMETHODIMP
828
InterceptedHttpChannel::GetInternalContentPolicyType(
829
nsContentPolicyType* aPolicyType) {
830
if (mLoadInfo) {
831
*aPolicyType = mLoadInfo->InternalContentPolicyType();
832
}
833
return NS_OK;
834
}
835
836
NS_IMETHODIMP
837
InterceptedHttpChannel::GetConsoleReportCollector(
838
nsIConsoleReportCollector** aConsoleReportCollector) {
839
nsCOMPtr<nsIConsoleReportCollector> ref(this);
840
ref.forget(aConsoleReportCollector);
841
return NS_OK;
842
}
843
844
NS_IMETHODIMP
845
InterceptedHttpChannel::GetLaunchServiceWorkerStart(
846
mozilla::TimeStamp* aTimeStamp) {
847
return HttpBaseChannel::GetLaunchServiceWorkerStart(aTimeStamp);
848
}
849
850
NS_IMETHODIMP
851
InterceptedHttpChannel::SetLaunchServiceWorkerStart(
852
mozilla::TimeStamp aTimeStamp) {
853
return HttpBaseChannel::SetLaunchServiceWorkerStart(aTimeStamp);
854
}
855
856
NS_IMETHODIMP
857
InterceptedHttpChannel::GetLaunchServiceWorkerEnd(
858
mozilla::TimeStamp* aTimeStamp) {
859
return HttpBaseChannel::GetLaunchServiceWorkerEnd(aTimeStamp);
860
}
861
862
NS_IMETHODIMP
863
InterceptedHttpChannel::SetLaunchServiceWorkerEnd(
864
mozilla::TimeStamp aTimeStamp) {
865
return HttpBaseChannel::SetLaunchServiceWorkerEnd(aTimeStamp);
866
}
867
868
NS_IMETHODIMP
869
InterceptedHttpChannel::SetDispatchFetchEventStart(
870
mozilla::TimeStamp aTimeStamp) {
871
return HttpBaseChannel::SetDispatchFetchEventStart(aTimeStamp);
872
}
873
874
NS_IMETHODIMP
875
InterceptedHttpChannel::SetDispatchFetchEventEnd(
876
mozilla::TimeStamp aTimeStamp) {
877
return HttpBaseChannel::SetDispatchFetchEventEnd(aTimeStamp);
878
}
879
880
NS_IMETHODIMP
881
InterceptedHttpChannel::SetHandleFetchEventStart(
882
mozilla::TimeStamp aTimeStamp) {
883
return HttpBaseChannel::SetHandleFetchEventStart(aTimeStamp);
884
}
885
886
NS_IMETHODIMP
887
InterceptedHttpChannel::SetHandleFetchEventEnd(mozilla::TimeStamp aTimeStamp) {
888
return HttpBaseChannel::SetHandleFetchEventEnd(aTimeStamp);
889
}
890
891
NS_IMETHODIMP
892
InterceptedHttpChannel::SetFinishResponseStart(mozilla::TimeStamp aTimeStamp) {
893
mFinishResponseStart = aTimeStamp;
894
return NS_OK;
895
}
896
897
NS_IMETHODIMP
898
InterceptedHttpChannel::SetFinishSynthesizedResponseEnd(
899
mozilla::TimeStamp aTimeStamp) {
900
MOZ_ASSERT(mSynthesizedOrReset == Invalid);
901
mSynthesizedOrReset = Synthesized;
902
mFinishResponseEnd = aTimeStamp;
903
return NS_OK;
904
}
905
906
NS_IMETHODIMP
907
InterceptedHttpChannel::SetChannelResetEnd(mozilla::TimeStamp aTimeStamp) {
908
MOZ_ASSERT(mSynthesizedOrReset == Invalid);
909
mSynthesizedOrReset = Reset;
910
mFinishResponseEnd = aTimeStamp;
911
return NS_OK;
912
}
913
914
NS_IMETHODIMP
915
InterceptedHttpChannel::SaveTimeStamps(void) {
916
// If we were not able to start the fetch event for some reason (like
917
// corrupted scripts), then just do nothing here.
918
if (mHandleFetchEventStart.IsNull()) {
919
return NS_OK;
920
}
921
922
bool isNonSubresourceRequest = nsContentUtils::IsNonSubresourceRequest(this);
923
nsCString navigationOrSubresource = isNonSubresourceRequest
924
? NS_LITERAL_CSTRING("navigation")
925
: NS_LITERAL_CSTRING("subresource");
926
927
nsAutoCString subresourceKey(EmptyCString());
928
GetSubresourceTimeStampKey(this, subresourceKey);
929
930
// We may have null timestamps if the fetch dispatch runnable was cancelled
931
// and we defaulted to resuming the request.
932
if (!mFinishResponseStart.IsNull() && !mFinishResponseEnd.IsNull()) {
933
Telemetry::HistogramID id =
934
(mSynthesizedOrReset == Synthesized)
935
? Telemetry::
936
SERVICE_WORKER_FETCH_EVENT_FINISH_SYNTHESIZED_RESPONSE_MS
937
: Telemetry::SERVICE_WORKER_FETCH_EVENT_CHANNEL_RESET_MS;
938
Telemetry::Accumulate(
939
id, navigationOrSubresource,
940
static_cast<uint32_t>(
941
(mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
942
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
943
Telemetry::Accumulate(
944
id, subresourceKey,
945
static_cast<uint32_t>(
946
(mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
947
}
948
}
949
950
Telemetry::Accumulate(
951
Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
952
navigationOrSubresource,
953
static_cast<uint32_t>((mHandleFetchEventStart - mDispatchFetchEventStart)
954
.ToMilliseconds()));
955
956
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
957
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
958
subresourceKey,
959
static_cast<uint32_t>((mHandleFetchEventStart -
960
mDispatchFetchEventStart)
961
.ToMilliseconds()));
962
}
963
964
if (!mFinishResponseEnd.IsNull()) {
965
Telemetry::Accumulate(
966
Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
967
navigationOrSubresource,
968
static_cast<uint32_t>(
969
(mFinishResponseEnd - mDispatchFetchEventStart).ToMilliseconds()));
970
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
971
Telemetry::Accumulate(
972
Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
973
subresourceKey,
974
static_cast<uint32_t>((mFinishResponseEnd - mDispatchFetchEventStart)
975
.ToMilliseconds()));
976
}
977
}
978
979
return NS_OK;
980
}
981
982
NS_IMETHODIMP
983
InterceptedHttpChannel::SetReleaseHandle(nsISupports* aHandle) {
984
mReleaseHandle = aHandle;
985
return NS_OK;
986
}
987
988
NS_IMETHODIMP
989
InterceptedHttpChannel::OnRedirectVerifyCallback(nsresult rv) {
990
MOZ_ASSERT(NS_IsMainThread());
991
992
if (NS_SUCCEEDED(rv)) {
993
rv = OpenRedirectChannel();
994
}
995
996
nsCOMPtr<nsIRedirectResultListener> hook;
997
GetCallback(hook);
998
if (hook) {
999
hook->OnRedirectResult(NS_SUCCEEDED(rv));
1000
}
1001
1002
if (NS_FAILED(rv)) {
1003
Cancel(rv);
1004
}
1005
1006
MaybeCallBodyCallback();
1007
1008
mIsPending = false;
1009
ReleaseListeners();
1010
1011
return NS_OK;
1012
}
1013
1014
NS_IMETHODIMP
1015
InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) {
1016
MOZ_ASSERT(NS_IsMainThread());
1017
1018
if (!mProgressSink) {
1019
GetCallback(mProgressSink);
1020
}
1021
1022
if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
1023
mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
1024
}
1025
1026
if (mListener) {
1027
return mListener->OnStartRequest(this);
1028
}
1029
return NS_OK;
1030
}
1031
1032
NS_IMETHODIMP
1033
InterceptedHttpChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
1034
MOZ_ASSERT(NS_IsMainThread());
1035
1036
if (NS_SUCCEEDED(mStatus)) {
1037
mStatus = aStatus;
1038
}
1039
1040
MaybeCallBodyCallback();
1041
1042
// Its possible that we have any async runnable queued to report some
1043
// progress when OnStopRequest() is triggered. Report any left over
1044
// progress immediately. The extra runnable will then do nothing thanks
1045
// to the ReleaseListeners() call below.
1046
MaybeCallStatusAndProgress();
1047
1048
mIsPending = false;
1049
1050
// Register entry to the PerformanceStorage resource timing
1051
MaybeReportTimingData();
1052
1053
nsresult rv = NS_OK;
1054
if (mListener) {
1055
rv = mListener->OnStopRequest(this, mStatus);
1056
}
1057
1058
gHttpHandler->OnStopRequest(this);
1059
1060
ReleaseListeners();
1061
1062
return rv;
1063
}
1064
1065
NS_IMETHODIMP
1066
InterceptedHttpChannel::OnDataAvailable(nsIRequest* aRequest,
1067
nsIInputStream* aInputStream,
1068
uint64_t aOffset, uint32_t aCount) {
1069
// Any thread if the channel has been retargeted.
1070
1071
if (mCanceled || !mListener) {
1072
// If there is no listener, we still need to drain the stream in order
1073
// maintain necko invariants.
1074
uint32_t unused = 0;
1075
aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &unused);
1076
return mStatus;
1077
}
1078
if (mProgressSink) {
1079
if (!(mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
1080
mProgress = aOffset + aCount;
1081
MaybeCallStatusAndProgress();
1082
}
1083
}
1084
1085
return mListener->OnDataAvailable(this, aInputStream, aOffset, aCount);
1086
}
1087
1088
NS_IMETHODIMP
1089
InterceptedHttpChannel::MessageDiversionStarted(
1090
ADivertableParentChannel* aParentChannel) {
1091
MOZ_ASSERT(!mParentChannel);
1092
mParentChannel = aParentChannel;
1093
mDiverting = true;
1094
uint32_t suspendCount = mSuspendCount;
1095
while (suspendCount--) {
1096
mParentChannel->SuspendMessageDiversion();
1097
}
1098
return NS_OK;
1099
}
1100
1101
NS_IMETHODIMP
1102
InterceptedHttpChannel::MessageDiversionStop() {
1103
MOZ_ASSERT(mParentChannel);
1104
mParentChannel = nullptr;
1105
mDiverting = false;
1106
return NS_OK;
1107
}
1108
1109
NS_IMETHODIMP
1110
InterceptedHttpChannel::SuspendInternal() {
1111
++mSuspendCount;
1112
if (mPump) {
1113
return mPump->Suspend();
1114
}
1115
return NS_OK;
1116
}
1117
1118
NS_IMETHODIMP
1119
InterceptedHttpChannel::ResumeInternal() {
1120
--mSuspendCount;
1121
if (mPump) {
1122
return mPump->Resume();
1123
}
1124
return NS_OK;
1125
}
1126
1127
NS_IMETHODIMP
1128
InterceptedHttpChannel::RetargetDeliveryTo(nsIEventTarget* aNewTarget) {
1129
MOZ_ASSERT(NS_IsMainThread());
1130
NS_ENSURE_ARG(aNewTarget);
1131
1132
// If retargeting to the main thread, do nothing.
1133
if (aNewTarget->IsOnCurrentThread()) {
1134
return NS_OK;
1135
}
1136
1137
// Retargeting is only valid during OnStartRequest for nsIChannels. So
1138
// we should only be called if we have a pump.
1139
if (!mPump) {
1140
return NS_ERROR_NOT_AVAILABLE;
1141
}
1142
1143
return mPump->RetargetDeliveryTo(aNewTarget);
1144
}
1145
1146
NS_IMETHODIMP
1147
InterceptedHttpChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget) {
1148
if (!mPump) {
1149
return NS_ERROR_NOT_AVAILABLE;
1150
}
1151
return mPump->GetDeliveryTarget(aEventTarget);
1152
}
1153
1154
NS_IMETHODIMP
1155
InterceptedHttpChannel::CheckListenerChain() {
1156
MOZ_ASSERT(NS_IsMainThread());
1157
nsresult rv = NS_OK;
1158
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
1159
do_QueryInterface(mListener, &rv);
1160
if (retargetableListener) {
1161
rv = retargetableListener->CheckListenerChain();
1162
}
1163
return rv;
1164
}
1165
1166
//-----------------------------------------------------------------------------
1167
// InterceptedHttpChannel::nsICacheInfoChannel
1168
//-----------------------------------------------------------------------------
1169
// InterceptedHttpChannel does not really implement the nsICacheInfoChannel
1170
// interface, we tranfers parameters to the saved
1171
// nsICacheInfoChannel(mSynthesizedCacheInfo) from StartSynthesizedResponse. And
1172
// we return false in IsFromCache and NS_ERROR_NOT_AVAILABLE for all other
1173
// methods while the saved mSynthesizedCacheInfo does not exist.
1174
NS_IMETHODIMP
1175
InterceptedHttpChannel::IsFromCache(bool* value) {
1176
if (mSynthesizedCacheInfo) {
1177
return mSynthesizedCacheInfo->IsFromCache(value);
1178
}
1179
*value = false;
1180
return NS_OK;
1181
}
1182
1183
NS_IMETHODIMP
1184
InterceptedHttpChannel::IsRacing(bool* value) {
1185
if (mSynthesizedCacheInfo) {
1186
return mSynthesizedCacheInfo->IsRacing(value);
1187
}
1188
*value = false;
1189
return NS_OK;
1190
}
1191
1192
NS_IMETHODIMP
1193
InterceptedHttpChannel::GetCacheEntryId(uint64_t* aCacheEntryId) {
1194
if (mSynthesizedCacheInfo) {
1195
return mSynthesizedCacheInfo->GetCacheEntryId(aCacheEntryId);
1196
}
1197
return NS_ERROR_NOT_AVAILABLE;
1198
}
1199
1200
NS_IMETHODIMP
1201
InterceptedHttpChannel::GetCacheTokenFetchCount(int32_t* _retval) {
1202
NS_ENSURE_ARG_POINTER(_retval);
1203
1204
if (mSynthesizedCacheInfo) {
1205
return mSynthesizedCacheInfo->GetCacheTokenFetchCount(_retval);
1206
}
1207
return NS_ERROR_NOT_AVAILABLE;
1208
}
1209
1210
NS_IMETHODIMP
1211
InterceptedHttpChannel::GetCacheTokenExpirationTime(uint32_t* _retval) {
1212
NS_ENSURE_ARG_POINTER(_retval);
1213
1214
if (mSynthesizedCacheInfo) {
1215
return mSynthesizedCacheInfo->GetCacheTokenExpirationTime(_retval);
1216
}
1217
return NS_ERROR_NOT_AVAILABLE;
1218
}
1219
1220
NS_IMETHODIMP
1221
InterceptedHttpChannel::GetCacheTokenCachedCharset(nsACString& _retval) {
1222
if (mSynthesizedCacheInfo) {
1223
return mSynthesizedCacheInfo->GetCacheTokenCachedCharset(_retval);
1224
}
1225
return NS_ERROR_NOT_AVAILABLE;
1226
}
1227
1228
NS_IMETHODIMP
1229
InterceptedHttpChannel::SetCacheTokenCachedCharset(const nsACString& aCharset) {
1230
if (mSynthesizedCacheInfo) {
1231
return mSynthesizedCacheInfo->SetCacheTokenCachedCharset(aCharset);
1232
}
1233
return NS_ERROR_NOT_AVAILABLE;
1234
}
1235
1236
NS_IMETHODIMP
1237
InterceptedHttpChannel::SetAllowStaleCacheContent(
1238
bool aAllowStaleCacheContent) {
1239
if (mSynthesizedCacheInfo) {
1240
return mSynthesizedCacheInfo->SetAllowStaleCacheContent(
1241
aAllowStaleCacheContent);
1242
}
1243
return NS_ERROR_NOT_AVAILABLE;
1244
}
1245
1246
NS_IMETHODIMP
1247
InterceptedHttpChannel::GetAllowStaleCacheContent(
1248
bool* aAllowStaleCacheContent) {
1249
if (mSynthesizedCacheInfo) {
1250
return mSynthesizedCacheInfo->GetAllowStaleCacheContent(
1251
aAllowStaleCacheContent);
1252
}
1253
return NS_ERROR_NOT_AVAILABLE;
1254
}
1255
1256
NS_IMETHODIMP
1257
InterceptedHttpChannel::GetPreferCacheLoadOverBypass(
1258
bool* aPreferCacheLoadOverBypass) {
1259
if (mSynthesizedCacheInfo) {
1260
return mSynthesizedCacheInfo->GetPreferCacheLoadOverBypass(
1261
aPreferCacheLoadOverBypass);
1262
}
1263
return NS_ERROR_NOT_AVAILABLE;
1264
}
1265
1266
NS_IMETHODIMP
1267
InterceptedHttpChannel::SetPreferCacheLoadOverBypass(
1268
bool aPreferCacheLoadOverBypass) {
1269
if (mSynthesizedCacheInfo) {
1270
return mSynthesizedCacheInfo->SetPreferCacheLoadOverBypass(
1271
aPreferCacheLoadOverBypass);
1272
}
1273
return NS_ERROR_NOT_AVAILABLE;
1274
}
1275
1276
NS_IMETHODIMP
1277
InterceptedHttpChannel::PreferAlternativeDataType(
1278
const nsACString& aType, const nsACString& aContentType,
1279
bool aDeliverAltData) {
1280
ENSURE_CALLED_BEFORE_ASYNC_OPEN();
1281
mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams(
1282
nsCString(aType), nsCString(aContentType), aDeliverAltData));
1283
return NS_OK;
1284
}
1285
1286
const nsTArray<PreferredAlternativeDataTypeParams>&
1287
InterceptedHttpChannel::PreferredAlternativeDataTypes() {
1288
return mPreferredCachedAltDataTypes;
1289
}
1290
1291
NS_IMETHODIMP
1292
InterceptedHttpChannel::GetAlternativeDataType(nsACString& aType) {
1293
if (mSynthesizedCacheInfo) {
1294
return mSynthesizedCacheInfo->GetAlternativeDataType(aType);
1295
}
1296
return NS_ERROR_NOT_AVAILABLE;
1297
}
1298
1299
NS_IMETHODIMP
1300
InterceptedHttpChannel::OpenAlternativeOutputStream(
1301
const nsACString& type, int64_t predictedSize,
1302
nsIAsyncOutputStream** _retval) {
1303
if (mSynthesizedCacheInfo) {
1304
return mSynthesizedCacheInfo->OpenAlternativeOutputStream(
1305
type, predictedSize, _retval);
1306
}
1307
return NS_ERROR_NOT_AVAILABLE;
1308
}
1309
1310
NS_IMETHODIMP
1311
InterceptedHttpChannel::GetOriginalInputStream(
1312
nsIInputStreamReceiver* aReceiver) {
1313
if (mSynthesizedCacheInfo) {
1314
return mSynthesizedCacheInfo->GetOriginalInputStream(aReceiver);
1315
}
1316
return NS_ERROR_NOT_AVAILABLE;
1317
}
1318
1319
NS_IMETHODIMP
1320
InterceptedHttpChannel::GetAltDataInputStream(
1321
const nsACString& aType, nsIInputStreamReceiver* aReceiver) {
1322
if (mSynthesizedCacheInfo) {
1323
return mSynthesizedCacheInfo->GetAltDataInputStream(aType, aReceiver);
1324
}
1325
return NS_ERROR_NOT_AVAILABLE;
1326
}
1327
1328
NS_IMETHODIMP
1329
InterceptedHttpChannel::GetCacheKey(uint32_t* key) {
1330
if (mSynthesizedCacheInfo) {
1331
return mSynthesizedCacheInfo->GetCacheKey(key);
1332
}
1333
return NS_ERROR_NOT_AVAILABLE;
1334
}
1335
1336
NS_IMETHODIMP
1337
InterceptedHttpChannel::SetCacheKey(uint32_t key) {
1338
if (mSynthesizedCacheInfo) {
1339
return mSynthesizedCacheInfo->SetCacheKey(key);
1340
}
1341
return NS_ERROR_NOT_AVAILABLE;
1342
}
1343
1344
} // namespace net
1345
} // namespace mozilla