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
rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags);
276
277
if (NS_FAILED(rv)) {
278
// Make sure to call the body callback since we took ownership
279
// above. Neither the new channel or our standard
280
// OnRedirectVerifyCallback() code will invoke the callback. Do it here.
281
bodyCallback->BodyComplete(rv);
282
283
OnRedirectVerifyCallback(rv);
284
}
285
286
return rv;
287
}
288
289
nsresult InterceptedHttpChannel::StartPump() {
290
MOZ_DIAGNOSTIC_ASSERT(!mPump);
291
MOZ_DIAGNOSTIC_ASSERT(mBodyReader);
292
293
// We don't support resuming an intercepted channel. We can't guarantee the
294
// ServiceWorker will always return the same data and we can't rely on the
295
// http cache code to detect changes. For now, just force the channel to
296
// NS_ERROR_NOT_RESUMABLE which should cause the front-end to recreate the
297
// channel without calling ResumeAt().
298
//
299
// It would also be possible to convert this information to a range request,
300
// but its unclear if we should do that for ServiceWorker FetchEvents. See:
301
//
303
if (mResumeStartPos > 0) {
304
return NS_ERROR_NOT_RESUMABLE;
305
}
306
307
// For progress we trust the content-length for the "maximum" size.
308
// We can't determine the full size from the stream itself since
309
// we may only receive the data incrementally. We can't trust
310
// Available() here.
311
// TODO: We could implement an nsIFixedLengthInputStream interface and
312
// QI to it here. This would let us determine the total length
313
// for streams that support it. See bug 1388774.
314
Unused << GetContentLength(&mSynthesizedStreamLength);
315
316
nsresult rv =
317
nsInputStreamPump::Create(getter_AddRefs(mPump), mBodyReader, 0, 0, true);
318
NS_ENSURE_SUCCESS(rv, rv);
319
320
rv = mPump->AsyncRead(this, nullptr);
321
NS_ENSURE_SUCCESS(rv, rv);
322
323
uint32_t suspendCount = mSuspendCount;
324
while (suspendCount--) {
325
mPump->Suspend();
326
}
327
328
MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
329
330
return rv;
331
}
332
333
nsresult InterceptedHttpChannel::OpenRedirectChannel() {
334
nsresult rv = NS_OK;
335
336
if (NS_FAILED(mStatus)) {
337
return mStatus;
338
}
339
340
if (!mRedirectChannel) {
341
return NS_ERROR_DOM_ABORT_ERR;
342
}
343
344
// Make sure to do this after we received redirect veto answer,
345
// i.e. after all sinks had been notified
346
mRedirectChannel->SetOriginalURI(mOriginalURI);
347
348
// open new channel
349
rv = mRedirectChannel->AsyncOpen(mListener);
350
NS_ENSURE_SUCCESS(rv, rv);
351
352
mStatus = NS_BINDING_REDIRECTED;
353
354
return rv;
355
}
356
357
void InterceptedHttpChannel::MaybeCallStatusAndProgress() {
358
// OnStatus() and OnProgress() must only be called on the main thread. If
359
// we are on a separate thread, then we maybe need to schedule a runnable
360
// to call them asynchronousnly.
361
if (!NS_IsMainThread()) {
362
// Check to see if we are already trying to call OnStatus/OnProgress
363
// asynchronously. If we are, then don't queue up another runnable.
364
// We don't want to flood the main thread.
365
if (mCallingStatusAndProgress) {
366
return;
367
}
368
mCallingStatusAndProgress = true;
369
370
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
371
"InterceptedHttpChannel::MaybeCallStatusAndProgress", this,
372
&InterceptedHttpChannel::MaybeCallStatusAndProgress);
373
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
374
375
return;
376
}
377
378
MOZ_ASSERT(NS_IsMainThread());
379
380
// We are about to capture out progress position. Clear the flag we use
381
// to de-duplicate progress report runnables. We want any further progress
382
// updates to trigger another runnable. We do this before capture the
383
// progress value since we're using atomics and not a mutex lock.
384
mCallingStatusAndProgress = false;
385
386
// Capture the current status from our atomic count.
387
int64_t progress = mProgress;
388
389
MOZ_DIAGNOSTIC_ASSERT(progress >= mProgressReported);
390
391
// Do nothing if we've already made the calls for this amount of progress
392
// or if the channel is not configured for these calls. Note, the check
393
// for mProgressSink here means we will not fire any spurious late calls
394
// after ReleaseListeners() is executed.
395
if (progress <= mProgressReported || mCanceled || !mProgressSink ||
396
(mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
397
return;
398
}
399
400
// Capture the host name on the first set of calls to avoid doing this
401
// string processing repeatedly.
402
if (mProgressReported == 0) {
403
nsAutoCString host;
404
MOZ_ALWAYS_SUCCEEDS(mURI->GetHost(host));
405
CopyUTF8toUTF16(host, mStatusHost);
406
}
407
408
mProgressSink->OnStatus(this, nullptr, NS_NET_STATUS_READING,
409
mStatusHost.get());
410
411
mProgressSink->OnProgress(this, nullptr, progress, mSynthesizedStreamLength);
412
413
mProgressReported = progress;
414
}
415
416
void InterceptedHttpChannel::MaybeCallBodyCallback() {
417
nsCOMPtr<nsIInterceptedBodyCallback> callback = mBodyCallback.forget();
418
if (callback) {
419
callback->BodyComplete(mStatus);
420
}
421
}
422
423
// static
424
already_AddRefed<InterceptedHttpChannel>
425
InterceptedHttpChannel::CreateForInterception(
426
PRTime aCreationTime, const TimeStamp& aCreationTimestamp,
427
const TimeStamp& aAsyncOpenTimestamp) {
428
// Create an InterceptedHttpChannel that will trigger a FetchEvent
429
// in a ServiceWorker when opened.
430
RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
431
aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
432
433
return ref.forget();
434
}
435
436
// static
437
already_AddRefed<InterceptedHttpChannel>
438
InterceptedHttpChannel::CreateForSynthesis(
439
const nsHttpResponseHead* aHead, nsIInputStream* aBody,
440
nsIInterceptedBodyCallback* aBodyCallback, PRTime aCreationTime,
441
const TimeStamp& aCreationTimestamp, const TimeStamp& aAsyncOpenTimestamp) {
442
MOZ_DIAGNOSTIC_ASSERT(aHead);
443
MOZ_DIAGNOSTIC_ASSERT(aBody);
444
445
// Create an InterceptedHttpChannel that already has a synthesized response.
446
// The synthetic response will be processed when opened. A FetchEvent
447
// will not be triggered.
448
RefPtr<InterceptedHttpChannel> ref = new InterceptedHttpChannel(
449
aCreationTime, aCreationTimestamp, aAsyncOpenTimestamp);
450
451
ref->mResponseHead = new nsHttpResponseHead(*aHead);
452
ref->mBodyReader = aBody;
453
ref->mBodyCallback = aBodyCallback;
454
455
return ref.forget();
456
}
457
458
NS_IMETHODIMP
459
InterceptedHttpChannel::Cancel(nsresult aStatus) {
460
// Note: This class has been designed to send all error results through
461
// Cancel(). Don't add calls directly to AsyncAbort() or
462
// DoNotifyListener(). Instead call Cancel().
463
464
if (mCanceled) {
465
return NS_OK;
466
}
467
mCanceled = true;
468
469
MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(aStatus));
470
if (NS_SUCCEEDED(mStatus)) {
471
mStatus = aStatus;
472
}
473
474
// Everything is suspended during diversion until it completes. Since the
475
// intercepted channel could be a long-running stream, we need to request that
476
// cancellation be triggered in the child, completing the diversion and
477
// allowing cancellation to run to completion.
478
if (mDiverting) {
479
Unused << mParentChannel->CancelDiversion();
480
// (We want the pump to be canceled as well, so don't directly return.)
481
}
482
483
if (mPump) {
484
return mPump->Cancel(mStatus);
485
}
486
487
return AsyncAbort(mStatus);
488
}
489
490
NS_IMETHODIMP
491
InterceptedHttpChannel::Suspend(void) {
492
nsresult rv = SuspendInternal();
493
494
nsresult rvParentChannel = NS_OK;
495
if (mParentChannel) {
496
rvParentChannel = mParentChannel->SuspendMessageDiversion();
497
}
498
499
return NS_FAILED(rv) ? rv : rvParentChannel;
500
}
501
502
NS_IMETHODIMP
503
InterceptedHttpChannel::Resume(void) {
504
nsresult rv = ResumeInternal();
505
506
nsresult rvParentChannel = NS_OK;
507
if (mParentChannel) {
508
rvParentChannel = mParentChannel->ResumeMessageDiversion();
509
}
510
511
return NS_FAILED(rv) ? rv : rvParentChannel;
512
}
513
514
NS_IMETHODIMP
515
InterceptedHttpChannel::GetSecurityInfo(nsISupports** aSecurityInfo) {
516
nsCOMPtr<nsISupports> ref(mSecurityInfo);
517
ref.forget(aSecurityInfo);
518
return NS_OK;
519
}
520
521
NS_IMETHODIMP
522
InterceptedHttpChannel::AsyncOpen(nsIStreamListener* aListener) {
523
nsCOMPtr<nsIStreamListener> listener(aListener);
524
nsresult rv =
525
nsContentSecurityManager::doContentSecurityCheck(this, listener);
526
if (NS_WARN_IF(NS_FAILED(rv))) {
527
Cancel(rv);
528
return rv;
529
}
530
if (mCanceled) {
531
return mStatus;
532
}
533
534
// After this point we should try to return NS_OK and notify the listener
535
// of the result.
536
mListener = aListener;
537
538
AsyncOpenInternal();
539
540
return NS_OK;
541
}
542
543
NS_IMETHODIMP
544
InterceptedHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage,
545
const nsACString& aCategory) {
546
// Synthetic responses should not trigger CORS blocking.
547
return NS_ERROR_NOT_IMPLEMENTED;
548
}
549
550
NS_IMETHODIMP
551
InterceptedHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
552
bool aWarning,
553
const nsAString& aURL,
554
const nsAString& aContentType) {
555
return NS_ERROR_NOT_IMPLEMENTED;
556
}
557
558
NS_IMETHODIMP
559
InterceptedHttpChannel::SetupFallbackChannel(const char* aFallbackKey) {
560
// AppCache should not be used with service worker intercepted channels.
561
// This should never be called.
562
return NS_ERROR_NOT_IMPLEMENTED;
563
}
564
565
NS_IMETHODIMP
566
InterceptedHttpChannel::SetPriority(int32_t aPriority) {
567
mPriority = clamped<int32_t>(aPriority, INT16_MIN, INT16_MAX);
568
return NS_OK;
569
}
570
571
NS_IMETHODIMP
572
InterceptedHttpChannel::SetClassFlags(uint32_t aClassFlags) {
573
mClassOfService = aClassFlags;
574
return NS_OK;
575
}
576
577
NS_IMETHODIMP
578
InterceptedHttpChannel::ClearClassFlags(uint32_t aClassFlags) {
579
mClassOfService &= ~aClassFlags;
580
return NS_OK;
581
}
582
583
NS_IMETHODIMP
584
InterceptedHttpChannel::AddClassFlags(uint32_t aClassFlags) {
585
mClassOfService |= aClassFlags;
586
return NS_OK;
587
}
588
589
NS_IMETHODIMP
590
InterceptedHttpChannel::ResumeAt(uint64_t aStartPos,
591
const nsACString& aEntityId) {
592
// We don't support resuming synthesized responses, but we do track this
593
// information so it can be passed on to the resulting nsHttpChannel if
594
// ResetInterception is called.
595
mResumeStartPos = aStartPos;
596
mResumeEntityId = aEntityId;
597
return NS_OK;
598
}
599
600
void InterceptedHttpChannel::DoNotifyListenerCleanup() {
601
// Prefer to cleanup in ReleaseListeners() as it seems to be called
602
// more consistently in necko.
603
}
604
605
void InterceptedHttpChannel::DoAsyncAbort(nsresult aStatus) {
606
Unused << AsyncAbort(aStatus);
607
}
608
609
NS_IMETHODIMP
610
InterceptedHttpChannel::ResetInterception(void) {
611
if (mCanceled) {
612
return mStatus;
613
}
614
615
uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
616
617
nsCOMPtr<nsIChannel> newChannel;
618
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
619
CloneLoadInfoForRedirect(mURI, flags);
620
nsresult rv =
621
NS_NewChannelInternal(getter_AddRefs(newChannel), mURI, redirectLoadInfo,
622
nullptr, // PerformanceStorage
623
nullptr, // aLoadGroup
624
nullptr, // aCallbacks
625
mLoadFlags);
626
NS_ENSURE_SUCCESS(rv, rv);
627
628
rv = SetupReplacementChannel(mURI, newChannel, true, flags);
629
NS_ENSURE_SUCCESS(rv, rv);
630
631
nsCOMPtr<nsITimedChannel> newTimedChannel = do_QueryInterface(newChannel);
632
if (newTimedChannel) {
633
if (!mAsyncOpenTime.IsNull()) {
634
newTimedChannel->SetAsyncOpen(mAsyncOpenTime);
635
}
636
if (!mChannelCreationTimestamp.IsNull()) {
637
newTimedChannel->SetChannelCreation(mChannelCreationTimestamp);
638
}
639
}
640
641
if (mRedirectMode != nsIHttpChannelInternal::REDIRECT_MODE_MANUAL) {
642
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
643
rv = newChannel->GetLoadFlags(&loadFlags);
644
NS_ENSURE_SUCCESS(rv, rv);
645
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
646
rv = newChannel->SetLoadFlags(loadFlags);
647
NS_ENSURE_SUCCESS(rv, rv);
648
}
649
650
mRedirectChannel = newChannel.forget();
651
652
rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags);
653
654
if (NS_FAILED(rv)) {
655
OnRedirectVerifyCallback(rv);
656
}
657
658
return rv;
659
}
660
661
NS_IMETHODIMP
662
InterceptedHttpChannel::SynthesizeStatus(uint16_t aStatus,
663
const nsACString& aReason) {
664
if (mCanceled) {
665
return mStatus;
666
}
667
668
if (!mSynthesizedResponseHead) {
669
mSynthesizedResponseHead.reset(new nsHttpResponseHead());
670
}
671
672
nsAutoCString statusLine;
673
statusLine.AppendLiteral("HTTP/1.1 ");
674
statusLine.AppendInt(aStatus);
675
statusLine.AppendLiteral(" ");
676
statusLine.Append(aReason);
677
678
mSynthesizedResponseHead->ParseStatusLine(statusLine);
679
return NS_OK;
680
}
681
682
NS_IMETHODIMP
683
InterceptedHttpChannel::SynthesizeHeader(const nsACString& aName,
684
const nsACString& aValue) {
685
if (mCanceled) {
686
return mStatus;
687
}
688
689
if (!mSynthesizedResponseHead) {
690
mSynthesizedResponseHead.reset(new nsHttpResponseHead());
691
}
692
693
nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
694
// Overwrite any existing header.
695
nsresult rv = mSynthesizedResponseHead->ParseHeaderLine(header);
696
NS_ENSURE_SUCCESS(rv, rv);
697
return NS_OK;
698
}
699
700
NS_IMETHODIMP
701
InterceptedHttpChannel::StartSynthesizedResponse(
702
nsIInputStream* aBody, nsIInterceptedBodyCallback* aBodyCallback,
703
nsICacheInfoChannel* aSynthesizedCacheInfo, const nsACString& aFinalURLSpec,
704
bool aResponseRedirected) {
705
nsresult rv = NS_OK;
706
707
auto autoCleanup = MakeScopeExit([&] {
708
// Auto-cancel on failure. Do this first to get mStatus set, if necessary.
709
if (NS_FAILED(rv)) {
710
Cancel(rv);
711
}
712
713
// If we early exit before taking ownership of the body, then automatically
714
// invoke the callback. This could be due to an error or because we're not
715
// going to consume it due to a redirect, etc.
716
if (aBodyCallback) {
717
aBodyCallback->BodyComplete(mStatus);
718
}
719
});
720
721
if (NS_FAILED(mStatus)) {
722
// Return NS_OK. The channel should fire callbacks with an error code
723
// if it was cancelled before this point.
724
return NS_OK;
725
}
726
727
// Take ownership of the body callbacks If a failure occurs we will
728
// automatically Cancel() the channel. This will then invoke OnStopRequest()
729
// which will invoke the correct callback. In the case of an opaque response
730
// redirect we pass ownership of the callback to the new channel.
731
mBodyCallback = aBodyCallback;
732
aBodyCallback = nullptr;
733
734
mSynthesizedCacheInfo = aSynthesizedCacheInfo;
735
736
if (!mSynthesizedResponseHead) {
737
mSynthesizedResponseHead.reset(new nsHttpResponseHead());
738
}
739
740
mResponseHead = mSynthesizedResponseHead.release();
741
742
if (ShouldRedirect()) {
743
rv = FollowSyntheticRedirect();
744
NS_ENSURE_SUCCESS(rv, rv);
745
746
return NS_OK;
747
}
748
749
// Intercepted responses should already be decoded.
750
SetApplyConversion(false);
751
752
// Errors and redirects may not have a body. Synthesize an empty string
753
// stream here so later code can be simpler.
754
mBodyReader = aBody;
755
if (!mBodyReader) {
756
rv = NS_NewCStringInputStream(getter_AddRefs(mBodyReader), EmptyCString());
757
NS_ENSURE_SUCCESS(rv, rv);
758
}
759
760
nsCOMPtr<nsIURI> responseURI;
761
if (!aFinalURLSpec.IsEmpty()) {
762
rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec);
763
NS_ENSURE_SUCCESS(rv, rv);
764
} else {
765
responseURI = mURI;
766
}
767
768
bool equal = false;
769
Unused << mURI->Equals(responseURI, &equal);
770
if (!equal) {
771
rv = RedirectForResponseURL(responseURI, aResponseRedirected);
772
NS_ENSURE_SUCCESS(rv, rv);
773
774
return NS_OK;
775
}
776
777
rv = StartPump();
778
NS_ENSURE_SUCCESS(rv, rv);
779
780
return NS_OK;
781
}
782
783
NS_IMETHODIMP
784
InterceptedHttpChannel::FinishSynthesizedResponse() {
785
if (mCanceled) {
786
// Return NS_OK. The channel should fire callbacks with an error code
787
// if it was cancelled before this point.
788
return NS_OK;
789
}
790
791
// TODO: Remove this API after interception moves to the parent process in
792
// e10s mode.
793
794
return NS_OK;
795
}
796
797
NS_IMETHODIMP
798
InterceptedHttpChannel::CancelInterception(nsresult aStatus) {
799
return Cancel(aStatus);
800
}
801
802
NS_IMETHODIMP
803
InterceptedHttpChannel::GetChannel(nsIChannel** aChannel) {
804
nsCOMPtr<nsIChannel> ref(this);
805
ref.forget(aChannel);
806
return NS_OK;
807
}
808
809
NS_IMETHODIMP
810
InterceptedHttpChannel::GetSecureUpgradedChannelURI(
811
nsIURI** aSecureUpgradedChannelURI) {
812
nsCOMPtr<nsIURI> ref(mURI);
813
ref.forget(aSecureUpgradedChannelURI);
814
return NS_OK;
815
}
816
817
NS_IMETHODIMP
818
InterceptedHttpChannel::SetChannelInfo(
819
mozilla::dom::ChannelInfo* aChannelInfo) {
820
return aChannelInfo->ResurrectInfoOnChannel(this);
821
}
822
823
NS_IMETHODIMP
824
InterceptedHttpChannel::GetInternalContentPolicyType(
825
nsContentPolicyType* aPolicyType) {
826
if (mLoadInfo) {
827
*aPolicyType = mLoadInfo->InternalContentPolicyType();
828
}
829
return NS_OK;
830
}
831
832
NS_IMETHODIMP
833
InterceptedHttpChannel::GetConsoleReportCollector(
834
nsIConsoleReportCollector** aConsoleReportCollector) {
835
nsCOMPtr<nsIConsoleReportCollector> ref(this);
836
ref.forget(aConsoleReportCollector);
837
return NS_OK;
838
}
839
840
NS_IMETHODIMP
841
InterceptedHttpChannel::GetLaunchServiceWorkerStart(
842
mozilla::TimeStamp* aTimeStamp) {
843
return HttpBaseChannel::GetLaunchServiceWorkerStart(aTimeStamp);
844
}
845
846
NS_IMETHODIMP
847
InterceptedHttpChannel::SetLaunchServiceWorkerStart(
848
mozilla::TimeStamp aTimeStamp) {
849
return HttpBaseChannel::SetLaunchServiceWorkerStart(aTimeStamp);
850
}
851
852
NS_IMETHODIMP
853
InterceptedHttpChannel::GetLaunchServiceWorkerEnd(
854
mozilla::TimeStamp* aTimeStamp) {
855
return HttpBaseChannel::GetLaunchServiceWorkerEnd(aTimeStamp);
856
}
857
858
NS_IMETHODIMP
859
InterceptedHttpChannel::SetLaunchServiceWorkerEnd(
860
mozilla::TimeStamp aTimeStamp) {
861
return HttpBaseChannel::SetLaunchServiceWorkerEnd(aTimeStamp);
862
}
863
864
NS_IMETHODIMP
865
InterceptedHttpChannel::SetDispatchFetchEventStart(
866
mozilla::TimeStamp aTimeStamp) {
867
return HttpBaseChannel::SetDispatchFetchEventStart(aTimeStamp);
868
}
869
870
NS_IMETHODIMP
871
InterceptedHttpChannel::SetDispatchFetchEventEnd(
872
mozilla::TimeStamp aTimeStamp) {
873
return HttpBaseChannel::SetDispatchFetchEventEnd(aTimeStamp);
874
}
875
876
NS_IMETHODIMP
877
InterceptedHttpChannel::SetHandleFetchEventStart(
878
mozilla::TimeStamp aTimeStamp) {
879
return HttpBaseChannel::SetHandleFetchEventStart(aTimeStamp);
880
}
881
882
NS_IMETHODIMP
883
InterceptedHttpChannel::SetHandleFetchEventEnd(mozilla::TimeStamp aTimeStamp) {
884
return HttpBaseChannel::SetHandleFetchEventEnd(aTimeStamp);
885
}
886
887
NS_IMETHODIMP
888
InterceptedHttpChannel::SetFinishResponseStart(mozilla::TimeStamp aTimeStamp) {
889
mFinishResponseStart = aTimeStamp;
890
return NS_OK;
891
}
892
893
NS_IMETHODIMP
894
InterceptedHttpChannel::SetFinishSynthesizedResponseEnd(
895
mozilla::TimeStamp aTimeStamp) {
896
MOZ_ASSERT(mSynthesizedOrReset == Invalid);
897
mSynthesizedOrReset = Synthesized;
898
mFinishResponseEnd = aTimeStamp;
899
return NS_OK;
900
}
901
902
NS_IMETHODIMP
903
InterceptedHttpChannel::SetChannelResetEnd(mozilla::TimeStamp aTimeStamp) {
904
MOZ_ASSERT(mSynthesizedOrReset == Invalid);
905
mSynthesizedOrReset = Reset;
906
mFinishResponseEnd = aTimeStamp;
907
return NS_OK;
908
}
909
910
NS_IMETHODIMP
911
InterceptedHttpChannel::SaveTimeStamps(void) {
912
// If we were not able to start the fetch event for some reason (like
913
// corrupted scripts), then just do nothing here.
914
if (mHandleFetchEventStart.IsNull()) {
915
return NS_OK;
916
}
917
918
bool isNonSubresourceRequest = nsContentUtils::IsNonSubresourceRequest(this);
919
nsCString navigationOrSubresource = isNonSubresourceRequest
920
? NS_LITERAL_CSTRING("navigation")
921
: NS_LITERAL_CSTRING("subresource");
922
923
nsAutoCString subresourceKey(EmptyCString());
924
GetSubresourceTimeStampKey(this, subresourceKey);
925
926
// We may have null timestamps if the fetch dispatch runnable was cancelled
927
// and we defaulted to resuming the request.
928
if (!mFinishResponseStart.IsNull() && !mFinishResponseEnd.IsNull()) {
929
Telemetry::HistogramID id =
930
(mSynthesizedOrReset == Synthesized)
931
? Telemetry::
932
SERVICE_WORKER_FETCH_EVENT_FINISH_SYNTHESIZED_RESPONSE_MS
933
: Telemetry::SERVICE_WORKER_FETCH_EVENT_CHANNEL_RESET_MS;
934
Telemetry::Accumulate(
935
id, navigationOrSubresource,
936
static_cast<uint32_t>(
937
(mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
938
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
939
Telemetry::Accumulate(
940
id, subresourceKey,
941
static_cast<uint32_t>(
942
(mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
943
}
944
}
945
946
Telemetry::Accumulate(
947
Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
948
navigationOrSubresource,
949
static_cast<uint32_t>((mHandleFetchEventStart - mDispatchFetchEventStart)
950
.ToMilliseconds()));
951
952
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
953
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
954
subresourceKey,
955
static_cast<uint32_t>((mHandleFetchEventStart -
956
mDispatchFetchEventStart)
957
.ToMilliseconds()));
958
}
959
960
if (!mFinishResponseEnd.IsNull()) {
961
Telemetry::Accumulate(
962
Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
963
navigationOrSubresource,
964
static_cast<uint32_t>(
965
(mFinishResponseEnd - mDispatchFetchEventStart).ToMilliseconds()));
966
if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
967
Telemetry::Accumulate(
968
Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
969
subresourceKey,
970
static_cast<uint32_t>((mFinishResponseEnd - mDispatchFetchEventStart)
971
.ToMilliseconds()));
972
}
973
}
974
975
return NS_OK;
976
}
977
978
NS_IMETHODIMP
979
InterceptedHttpChannel::SetReleaseHandle(nsISupports* aHandle) {
980
mReleaseHandle = aHandle;
981
return NS_OK;
982
}
983
984
NS_IMETHODIMP
985
InterceptedHttpChannel::OnRedirectVerifyCallback(nsresult rv) {
986
MOZ_ASSERT(NS_IsMainThread());
987
988
if (NS_SUCCEEDED(rv)) {
989
rv = OpenRedirectChannel();
990
}
991
992
nsCOMPtr<nsIRedirectResultListener> hook;
993
GetCallback(hook);
994
if (hook) {
995
hook->OnRedirectResult(NS_SUCCEEDED(rv));
996
}
997
998
if (NS_FAILED(rv)) {
999
Cancel(rv);
1000
}
1001
1002
MaybeCallBodyCallback();
1003
1004
mIsPending = false;
1005
ReleaseListeners();
1006
1007
return NS_OK;
1008
}
1009
1010
NS_IMETHODIMP
1011
InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) {
1012
MOZ_ASSERT(NS_IsMainThread());
1013
1014
if (!mProgressSink) {
1015
GetCallback(mProgressSink);
1016
}
1017
1018
if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
1019
mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
1020
}
1021
1022
if (mListener) {
1023
return mListener->OnStartRequest(this);
1024
}
1025
return NS_OK;
1026
}
1027
1028
NS_IMETHODIMP
1029
InterceptedHttpChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
1030
MOZ_ASSERT(NS_IsMainThread());
1031
1032
if (NS_SUCCEEDED(mStatus)) {
1033
mStatus = aStatus;
1034
}
1035
1036
MaybeCallBodyCallback();
1037
1038
// Its possible that we have any async runnable queued to report some
1039
// progress when OnStopRequest() is triggered. Report any left over
1040
// progress immediately. The extra runnable will then do nothing thanks
1041
// to the ReleaseListeners() call below.
1042
MaybeCallStatusAndProgress();
1043
1044
mIsPending = false;
1045
1046
// Register entry to the PerformanceStorage resource timing
1047
MaybeReportTimingData();
1048
1049
nsresult rv = NS_OK;
1050
if (mListener) {
1051
rv = mListener->OnStopRequest(this, mStatus);
1052
}
1053
1054
gHttpHandler->OnStopRequest(this);
1055
1056
ReleaseListeners();
1057
1058
return rv;
1059
}
1060
1061
NS_IMETHODIMP
1062
InterceptedHttpChannel::OnDataAvailable(nsIRequest* aRequest,
1063
nsIInputStream* aInputStream,
1064
uint64_t aOffset, uint32_t aCount) {
1065
// Any thread if the channel has been retargeted.
1066
1067
if (mCanceled || !mListener) {
1068
// If there is no listener, we still need to drain the stream in order
1069
// maintain necko invariants.
1070
uint32_t unused = 0;
1071
aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &unused);
1072
return mStatus;
1073
}
1074
if (mProgressSink) {
1075
if (!(mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) {
1076
mProgress = aOffset + aCount;
1077
MaybeCallStatusAndProgress();
1078
}
1079
}
1080
1081
return mListener->OnDataAvailable(this, aInputStream, aOffset, aCount);
1082
}
1083
1084
NS_IMETHODIMP
1085
InterceptedHttpChannel::MessageDiversionStarted(
1086
ADivertableParentChannel* aParentChannel) {
1087
MOZ_ASSERT(!mParentChannel);
1088
mParentChannel = aParentChannel;
1089
mDiverting = true;
1090
uint32_t suspendCount = mSuspendCount;
1091
while (suspendCount--) {
1092
mParentChannel->SuspendMessageDiversion();
1093
}
1094
return NS_OK;
1095
}
1096
1097
NS_IMETHODIMP
1098
InterceptedHttpChannel::MessageDiversionStop() {
1099
MOZ_ASSERT(mParentChannel);
1100
mParentChannel = nullptr;
1101
mDiverting = false;
1102
return NS_OK;
1103
}
1104
1105
NS_IMETHODIMP
1106
InterceptedHttpChannel::SuspendInternal() {
1107
++mSuspendCount;
1108
if (mPump) {
1109
return mPump->Suspend();
1110
}
1111
return NS_OK;
1112
}
1113
1114
NS_IMETHODIMP
1115
InterceptedHttpChannel::ResumeInternal() {
1116
--mSuspendCount;
1117
if (mPump) {
1118
return mPump->Resume();
1119
}
1120
return NS_OK;
1121
}
1122
1123
NS_IMETHODIMP
1124
InterceptedHttpChannel::RetargetDeliveryTo(nsIEventTarget* aNewTarget) {
1125
MOZ_ASSERT(NS_IsMainThread());
1126
NS_ENSURE_ARG(aNewTarget);
1127
1128
// If retargeting to the main thread, do nothing.
1129
if (aNewTarget->IsOnCurrentThread()) {
1130
return NS_OK;
1131
}
1132
1133
// Retargeting is only valid during OnStartRequest for nsIChannels. So
1134
// we should only be called if we have a pump.
1135
if (!mPump) {
1136
return NS_ERROR_NOT_AVAILABLE;
1137
}
1138
1139
return mPump->RetargetDeliveryTo(aNewTarget);
1140
}
1141
1142
NS_IMETHODIMP
1143
InterceptedHttpChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget) {
1144
if (!mPump) {
1145
return NS_ERROR_NOT_AVAILABLE;
1146
}
1147
return mPump->GetDeliveryTarget(aEventTarget);
1148
}
1149
1150
NS_IMETHODIMP
1151
InterceptedHttpChannel::CheckListenerChain() {
1152
MOZ_ASSERT(NS_IsMainThread());
1153
nsresult rv = NS_OK;
1154
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
1155
do_QueryInterface(mListener, &rv);
1156
if (retargetableListener) {
1157
rv = retargetableListener->CheckListenerChain();
1158
}
1159
return rv;
1160
}
1161
1162
//-----------------------------------------------------------------------------
1163
// InterceptedHttpChannel::nsICacheInfoChannel
1164
//-----------------------------------------------------------------------------
1165
// InterceptedHttpChannel does not really implement the nsICacheInfoChannel
1166
// interface, we tranfers parameters to the saved
1167
// nsICacheInfoChannel(mSynthesizedCacheInfo) from StartSynthesizedResponse. And
1168
// we return false in IsFromCache and NS_ERROR_NOT_AVAILABLE for all other
1169
// methods while the saved mSynthesizedCacheInfo does not exist.
1170
NS_IMETHODIMP
1171
InterceptedHttpChannel::IsFromCache(bool* value) {
1172
if (mSynthesizedCacheInfo) {
1173
return mSynthesizedCacheInfo->IsFromCache(value);
1174
}
1175
*value = false;
1176
return NS_OK;
1177
}
1178
1179
NS_IMETHODIMP
1180
InterceptedHttpChannel::IsRacing(bool* value) {
1181
if (mSynthesizedCacheInfo) {
1182
return mSynthesizedCacheInfo->IsRacing(value);
1183
}
1184
*value = false;
1185
return NS_OK;
1186
}
1187
1188
NS_IMETHODIMP
1189
InterceptedHttpChannel::GetCacheEntryId(uint64_t* aCacheEntryId) {
1190
if (mSynthesizedCacheInfo) {
1191
return mSynthesizedCacheInfo->GetCacheEntryId(aCacheEntryId);
1192
}
1193
return NS_ERROR_NOT_AVAILABLE;
1194
}
1195
1196
NS_IMETHODIMP
1197
InterceptedHttpChannel::GetCacheTokenFetchCount(int32_t* _retval) {
1198
NS_ENSURE_ARG_POINTER(_retval);
1199
1200
if (mSynthesizedCacheInfo) {
1201
return mSynthesizedCacheInfo->GetCacheTokenFetchCount(_retval);
1202
}
1203
return NS_ERROR_NOT_AVAILABLE;
1204
}
1205
1206
NS_IMETHODIMP
1207
InterceptedHttpChannel::GetCacheTokenExpirationTime(uint32_t* _retval) {
1208
NS_ENSURE_ARG_POINTER(_retval);
1209
1210
if (mSynthesizedCacheInfo) {
1211
return mSynthesizedCacheInfo->GetCacheTokenExpirationTime(_retval);
1212
}
1213
return NS_ERROR_NOT_AVAILABLE;
1214
}
1215
1216
NS_IMETHODIMP
1217
InterceptedHttpChannel::GetCacheTokenCachedCharset(nsACString& _retval) {
1218
if (mSynthesizedCacheInfo) {
1219
return mSynthesizedCacheInfo->GetCacheTokenCachedCharset(_retval);
1220
}
1221
return NS_ERROR_NOT_AVAILABLE;
1222
}
1223
1224
NS_IMETHODIMP
1225
InterceptedHttpChannel::SetCacheTokenCachedCharset(const nsACString& aCharset) {
1226
if (mSynthesizedCacheInfo) {
1227
return mSynthesizedCacheInfo->SetCacheTokenCachedCharset(aCharset);
1228
}
1229
return NS_ERROR_NOT_AVAILABLE;
1230
}
1231
1232
NS_IMETHODIMP
1233
InterceptedHttpChannel::SetAllowStaleCacheContent(
1234
bool aAllowStaleCacheContent) {
1235
if (mSynthesizedCacheInfo) {
1236
return mSynthesizedCacheInfo->SetAllowStaleCacheContent(
1237
aAllowStaleCacheContent);
1238
}
1239
return NS_ERROR_NOT_AVAILABLE;
1240
}
1241
1242
NS_IMETHODIMP
1243
InterceptedHttpChannel::GetAllowStaleCacheContent(
1244
bool* aAllowStaleCacheContent) {
1245
if (mSynthesizedCacheInfo) {
1246
return mSynthesizedCacheInfo->GetAllowStaleCacheContent(
1247
aAllowStaleCacheContent);
1248
}
1249
return NS_ERROR_NOT_AVAILABLE;
1250
}
1251
1252
NS_IMETHODIMP
1253
InterceptedHttpChannel::PreferAlternativeDataType(
1254
const nsACString& aType, const nsACString& aContentType,
1255
bool aDeliverAltData) {
1256
ENSURE_CALLED_BEFORE_ASYNC_OPEN();
1257
mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams(
1258
nsCString(aType), nsCString(aContentType), aDeliverAltData));
1259
return NS_OK;
1260
}
1261
1262
const nsTArray<PreferredAlternativeDataTypeParams>&
1263
InterceptedHttpChannel::PreferredAlternativeDataTypes() {
1264
return mPreferredCachedAltDataTypes;
1265
}
1266
1267
NS_IMETHODIMP
1268
InterceptedHttpChannel::GetAlternativeDataType(nsACString& aType) {
1269
if (mSynthesizedCacheInfo) {
1270
return mSynthesizedCacheInfo->GetAlternativeDataType(aType);
1271
}
1272
return NS_ERROR_NOT_AVAILABLE;
1273
}
1274
1275
NS_IMETHODIMP
1276
InterceptedHttpChannel::OpenAlternativeOutputStream(
1277
const nsACString& type, int64_t predictedSize,
1278
nsIAsyncOutputStream** _retval) {
1279
if (mSynthesizedCacheInfo) {
1280
return mSynthesizedCacheInfo->OpenAlternativeOutputStream(
1281
type, predictedSize, _retval);
1282
}
1283
return NS_ERROR_NOT_AVAILABLE;
1284
}
1285
1286
NS_IMETHODIMP
1287
InterceptedHttpChannel::GetOriginalInputStream(
1288
nsIInputStreamReceiver* aReceiver) {
1289
if (mSynthesizedCacheInfo) {
1290
return mSynthesizedCacheInfo->GetOriginalInputStream(aReceiver);
1291
}
1292
return NS_ERROR_NOT_AVAILABLE;
1293
}
1294
1295
NS_IMETHODIMP
1296
InterceptedHttpChannel::GetAltDataInputStream(
1297
const nsACString& aType, nsIInputStreamReceiver* aReceiver) {
1298
if (mSynthesizedCacheInfo) {
1299
return mSynthesizedCacheInfo->GetAltDataInputStream(aType, aReceiver);
1300
}
1301
return NS_ERROR_NOT_AVAILABLE;
1302
}
1303
1304
NS_IMETHODIMP
1305
InterceptedHttpChannel::GetCacheKey(uint32_t* key) {
1306
if (mSynthesizedCacheInfo) {
1307
return mSynthesizedCacheInfo->GetCacheKey(key);
1308
}
1309
return NS_ERROR_NOT_AVAILABLE;
1310
}
1311
1312
NS_IMETHODIMP
1313
InterceptedHttpChannel::SetCacheKey(uint32_t key) {
1314
if (mSynthesizedCacheInfo) {
1315
return mSynthesizedCacheInfo->SetCacheKey(key);
1316
}
1317
return NS_ERROR_NOT_AVAILABLE;
1318
}
1319
1320
} // namespace net
1321
} // namespace mozilla