Coverage Report

Created: 2018-09-25 14:53

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