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