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