Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/nsBaseChannel.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set sw=2 sts=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 "nsBaseChannel.h"
8
#include "nsContentUtils.h"
9
#include "nsURLHelper.h"
10
#include "nsNetCID.h"
11
#include "nsMimeTypes.h"
12
#include "nsUnknownDecoder.h"
13
#include "nsIScriptSecurityManager.h"
14
#include "nsMimeTypes.h"
15
#include "nsIHttpChannel.h"
16
#include "nsIChannelEventSink.h"
17
#include "nsIStreamConverterService.h"
18
#include "nsChannelClassifier.h"
19
#include "nsAsyncRedirectVerifyHelper.h"
20
#include "nsProxyRelease.h"
21
#include "nsXULAppAPI.h"
22
#include "nsContentSecurityManager.h"
23
#include "LoadInfo.h"
24
#include "nsServiceManagerUtils.h"
25
#include "nsRedirectHistoryEntry.h"
26
27
// This class is used to suspend a request across a function scope.
28
class ScopedRequestSuspender {
29
public:
30
  explicit ScopedRequestSuspender(nsIRequest *request)
31
0
    : mRequest(request) {
32
0
    if (mRequest && NS_FAILED(mRequest->Suspend())) {
33
0
      NS_WARNING("Couldn't suspend pump");
34
0
      mRequest = nullptr;
35
0
    }
36
0
  }
37
0
  ~ScopedRequestSuspender() {
38
0
    if (mRequest)
39
0
      mRequest->Resume();
40
0
  }
41
private:
42
  nsIRequest *mRequest;
43
};
44
45
// Used to suspend data events from mRequest within a function scope.  This is
46
// usually needed when a function makes callbacks that could process events.
47
#define SUSPEND_PUMP_FOR_SCOPE() \
48
0
  ScopedRequestSuspender pump_suspender__(mRequest)
49
50
//-----------------------------------------------------------------------------
51
// nsBaseChannel
52
53
nsBaseChannel::nsBaseChannel()
54
  : NeckoTargetHolder(nullptr)
55
  , mPumpingData(false)
56
  , mLoadFlags(LOAD_NORMAL)
57
  , mQueriedProgressSink(true)
58
  , mSynthProgressEvents(false)
59
  , mAllowThreadRetargeting(true)
60
  , mWaitingOnAsyncRedirect(false)
61
  , mOpenRedirectChannel(false)
62
  , mRedirectFlags{ 0 }
63
  , mStatus(NS_OK)
64
  , mContentDispositionHint(UINT32_MAX)
65
  , mContentLength(-1)
66
  , mWasOpened(false)
67
0
{
68
0
  mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
69
0
}
70
71
nsBaseChannel::~nsBaseChannel()
72
0
{
73
0
  NS_ReleaseOnMainThreadSystemGroup(
74
0
    "nsBaseChannel::mLoadInfo", mLoadInfo.forget());
75
0
}
76
77
nsresult
78
nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags,
79
                        bool openNewChannel)
80
0
{
81
0
  SUSPEND_PUMP_FOR_SCOPE();
82
0
83
0
  // Transfer properties
84
0
85
0
  newChannel->SetLoadGroup(mLoadGroup);
86
0
  newChannel->SetNotificationCallbacks(mCallbacks);
87
0
  newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE);
88
0
89
0
  // make a copy of the loadinfo, append to the redirectchain
90
0
  // and set it on the new channel
91
0
  if (mLoadInfo) {
92
0
    nsSecurityFlags secFlags = mLoadInfo->GetSecurityFlags() &
93
0
                               ~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
94
0
    nsCOMPtr<nsILoadInfo> newLoadInfo =
95
0
      static_cast<mozilla::net::LoadInfo*>(mLoadInfo.get())->CloneWithNewSecFlags(secFlags);
96
0
97
0
    nsCOMPtr<nsIPrincipal> uriPrincipal;
98
0
    nsIScriptSecurityManager *sm = nsContentUtils::GetSecurityManager();
99
0
    sm->GetChannelURIPrincipal(this, getter_AddRefs(uriPrincipal));
100
0
    bool isInternalRedirect =
101
0
      (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
102
0
                        nsIChannelEventSink::REDIRECT_STS_UPGRADE));
103
0
104
0
    // nsBaseChannel hst no thing to do with HttpBaseChannel, we would not care
105
0
    // about referrer and remote address in this case
106
0
    nsCOMPtr<nsIRedirectHistoryEntry> entry =
107
0
      new nsRedirectHistoryEntry(uriPrincipal, nullptr, EmptyCString());
108
0
109
0
    newLoadInfo->AppendRedirectHistoryEntry(entry, isInternalRedirect);
110
0
111
0
    // Ensure the channel's loadInfo's result principal URI so that it's
112
0
    // either non-null or updated to the redirect target URI.
113
0
    // We must do this because in case the loadInfo's result principal URI
114
0
    // is null, it would be taken from OriginalURI of the channel.  But we
115
0
    // overwrite it with the whole redirect chain first URI before opening
116
0
    // the target channel, hence the information would be lost.
117
0
    // If the protocol handler that created the channel wants to use
118
0
    // the originalURI of the channel as the principal URI, it has left
119
0
    // the result principal URI on the load info null.
120
0
    nsCOMPtr<nsIURI> resultPrincipalURI;
121
0
122
0
    nsCOMPtr<nsILoadInfo> existingLoadInfo = newChannel->GetLoadInfo();
123
0
    if (existingLoadInfo) {
124
0
      existingLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
125
0
    }
126
0
    if (!resultPrincipalURI) {
127
0
      newChannel->GetOriginalURI(getter_AddRefs(resultPrincipalURI));
128
0
    }
129
0
130
0
    newLoadInfo->SetResultPrincipalURI(resultPrincipalURI);
131
0
132
0
    newChannel->SetLoadInfo(newLoadInfo);
133
0
  }
134
0
  else {
135
0
    // the newChannel was created with a dummy loadInfo, we should clear
136
0
    // it in case the original channel does not have a loadInfo
137
0
    newChannel->SetLoadInfo(nullptr);
138
0
  }
139
0
140
0
  // Preserve the privacy bit if it has been overridden
141
0
  if (mPrivateBrowsingOverriden) {
142
0
    nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
143
0
      do_QueryInterface(newChannel);
144
0
    if (newPBChannel) {
145
0
      newPBChannel->SetPrivate(mPrivateBrowsing);
146
0
    }
147
0
  }
148
0
149
0
  nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel);
150
0
  if (bag) {
151
0
    for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
152
0
      bag->SetProperty(iter.Key(), iter.UserData());
153
0
    }
154
0
  }
155
0
156
0
  // Notify consumer, giving chance to cancel redirect.
157
0
158
0
  RefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
159
0
      new nsAsyncRedirectVerifyHelper();
160
0
161
0
  bool checkRedirectSynchronously = !openNewChannel;
162
0
  nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
163
0
164
0
  mRedirectChannel = newChannel;
165
0
  mRedirectFlags = redirectFlags;
166
0
  mOpenRedirectChannel = openNewChannel;
167
0
  nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags,
168
0
                                             target, checkRedirectSynchronously);
169
0
  if (NS_FAILED(rv))
170
0
    return rv;
171
0
172
0
  if (checkRedirectSynchronously && NS_FAILED(mStatus))
173
0
    return mStatus;
174
0
175
0
  return NS_OK;
176
0
}
177
178
nsresult
179
nsBaseChannel::ContinueRedirect()
180
0
{
181
0
  // Make sure to do this _after_ making all the OnChannelRedirect calls
182
0
  mRedirectChannel->SetOriginalURI(OriginalURI());
183
0
184
0
  // If we fail to open the new channel, then we want to leave this channel
185
0
  // unaffected, so we defer tearing down our channel until we have succeeded
186
0
  // with the redirect.
187
0
188
0
  if (mOpenRedirectChannel) {
189
0
    nsresult rv = NS_OK;
190
0
    if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
191
0
      MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
192
0
      rv = mRedirectChannel->AsyncOpen2(mListener);
193
0
    }
194
0
    else {
195
0
      rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
196
0
    }
197
0
    NS_ENSURE_SUCCESS(rv, rv);
198
0
  }
199
0
200
0
  mRedirectChannel = nullptr;
201
0
202
0
  // close down this channel
203
0
  Cancel(NS_BINDING_REDIRECTED);
204
0
  ChannelDone();
205
0
206
0
  return NS_OK;
207
0
}
208
209
bool
210
nsBaseChannel::HasContentTypeHint() const
211
0
{
212
0
  NS_ASSERTION(!Pending(), "HasContentTypeHint called too late");
213
0
  return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE);
214
0
}
215
216
nsresult
217
nsBaseChannel::PushStreamConverter(const char *fromType,
218
                                   const char *toType,
219
                                   bool invalidatesContentLength,
220
                                   nsIStreamListener **result)
221
0
{
222
0
  NS_ASSERTION(mListener, "no listener");
223
0
224
0
  nsresult rv;
225
0
  nsCOMPtr<nsIStreamConverterService> scs =
226
0
      do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
227
0
  if (NS_FAILED(rv))
228
0
    return rv;
229
0
230
0
  nsCOMPtr<nsIStreamListener> converter;
231
0
  rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext,
232
0
                             getter_AddRefs(converter));
233
0
  if (NS_SUCCEEDED(rv)) {
234
0
    mListener = converter;
235
0
    if (invalidatesContentLength)
236
0
      mContentLength = -1;
237
0
    if (result) {
238
0
      *result = nullptr;
239
0
      converter.swap(*result);
240
0
    }
241
0
  }
242
0
  return rv;
243
0
}
244
245
nsresult
246
nsBaseChannel::BeginPumpingData()
247
0
{
248
0
  nsresult rv;
249
0
250
0
  rv = BeginAsyncRead(this, getter_AddRefs(mRequest));
251
0
  if (NS_SUCCEEDED(rv)) {
252
0
    mPumpingData = true;
253
0
    return NS_OK;
254
0
  }
255
0
  if (rv != NS_ERROR_NOT_IMPLEMENTED) {
256
0
    return rv;
257
0
  }
258
0
259
0
  nsCOMPtr<nsIInputStream> stream;
260
0
  nsCOMPtr<nsIChannel> channel;
261
0
  rv = OpenContentStream(true, getter_AddRefs(stream),
262
0
                         getter_AddRefs(channel));
263
0
  if (NS_FAILED(rv))
264
0
    return rv;
265
0
266
0
  NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?");
267
0
268
0
  if (channel) {
269
0
      nsCOMPtr<nsIRunnable> runnable = new RedirectRunnable(this, channel);
270
0
      rv = Dispatch(runnable.forget());
271
0
      if (NS_SUCCEEDED(rv))
272
0
          mWaitingOnAsyncRedirect = true;
273
0
      return rv;
274
0
  }
275
0
276
0
  // By assigning mPump, we flag this channel as pending (see Pending).  It's
277
0
  // important that the pending flag is set when we call into the stream (the
278
0
  // call to AsyncRead results in the stream's AsyncWait method being called)
279
0
  // and especially when we call into the loadgroup.  Our caller takes care to
280
0
  // release mPump if we return an error.
281
0
282
0
  nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
283
0
  rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, 0, 0, true,
284
0
                                 target);
285
0
  if (NS_SUCCEEDED(rv)) {
286
0
    mPumpingData = true;
287
0
    mRequest = mPump;
288
0
    rv = mPump->AsyncRead(this, nullptr);
289
0
  }
290
0
291
0
  return rv;
292
0
}
293
294
void
295
nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
296
0
{
297
0
  NS_ASSERTION(!mPumpingData, "Shouldn't have gotten here");
298
0
299
0
  nsresult rv = mStatus;
300
0
  if (NS_SUCCEEDED(mStatus)) {
301
0
    rv = Redirect(newChannel,
302
0
                  nsIChannelEventSink::REDIRECT_TEMPORARY,
303
0
                  true);
304
0
    if (NS_SUCCEEDED(rv)) {
305
0
      // OnRedirectVerifyCallback will be called asynchronously
306
0
      return;
307
0
    }
308
0
  }
309
0
310
0
  ContinueHandleAsyncRedirect(rv);
311
0
}
312
313
void
314
nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result)
315
0
{
316
0
  mWaitingOnAsyncRedirect = false;
317
0
318
0
  if (NS_FAILED(result))
319
0
    Cancel(result);
320
0
321
0
  if (NS_FAILED(result) && mListener) {
322
0
    // Notify our consumer ourselves
323
0
    mListener->OnStartRequest(this, mListenerContext);
324
0
    mListener->OnStopRequest(this, mListenerContext, mStatus);
325
0
    ChannelDone();
326
0
  }
327
0
328
0
  if (mLoadGroup)
329
0
    mLoadGroup->RemoveRequest(this, nullptr, mStatus);
330
0
331
0
  // Drop notification callbacks to prevent cycles.
332
0
  mCallbacks = nullptr;
333
0
  CallbacksChanged();
334
0
}
335
336
void
337
nsBaseChannel::ClassifyURI()
338
0
{
339
0
  // For channels created in the child process, delegate to the parent to
340
0
  // classify URIs.
341
0
  if (!XRE_IsParentProcess()) {
342
0
    return;
343
0
  }
344
0
345
0
  if (mLoadFlags & LOAD_CLASSIFY_URI) {
346
0
    RefPtr<nsChannelClassifier> classifier = new nsChannelClassifier(this);
347
0
    if (classifier) {
348
0
      classifier->Start();
349
0
    } else {
350
0
      Cancel(NS_ERROR_OUT_OF_MEMORY);
351
0
    }
352
0
  }
353
0
}
354
355
//-----------------------------------------------------------------------------
356
// nsBaseChannel::nsISupports
357
358
NS_IMPL_ISUPPORTS_INHERITED(nsBaseChannel,
359
                            nsHashPropertyBag,
360
                            nsIRequest,
361
                            nsIChannel,
362
                            nsIThreadRetargetableRequest,
363
                            nsIInterfaceRequestor,
364
                            nsITransportEventSink,
365
                            nsIRequestObserver,
366
                            nsIStreamListener,
367
                            nsIThreadRetargetableStreamListener,
368
                            nsIAsyncVerifyRedirectCallback,
369
                            nsIPrivateBrowsingChannel)
370
371
//-----------------------------------------------------------------------------
372
// nsBaseChannel::nsIRequest
373
374
NS_IMETHODIMP
375
nsBaseChannel::GetName(nsACString &result)
376
0
{
377
0
  if (!mURI) {
378
0
    result.Truncate();
379
0
    return NS_OK;
380
0
  }
381
0
  return mURI->GetSpec(result);
382
0
}
383
384
NS_IMETHODIMP
385
nsBaseChannel::IsPending(bool *result)
386
0
{
387
0
  *result = Pending();
388
0
  return NS_OK;
389
0
}
390
391
NS_IMETHODIMP
392
nsBaseChannel::GetStatus(nsresult *status)
393
0
{
394
0
  if (mRequest && NS_SUCCEEDED(mStatus)) {
395
0
    mRequest->GetStatus(status);
396
0
  } else {
397
0
    *status = mStatus;
398
0
  }
399
0
  return NS_OK;
400
0
}
401
402
NS_IMETHODIMP
403
nsBaseChannel::Cancel(nsresult status)
404
0
{
405
0
  // Ignore redundant cancelation
406
0
  if (NS_FAILED(mStatus))
407
0
    return NS_OK;
408
0
409
0
  mStatus = status;
410
0
411
0
  if (mRequest)
412
0
    mRequest->Cancel(status);
413
0
414
0
  return NS_OK;
415
0
}
416
417
NS_IMETHODIMP
418
nsBaseChannel::Suspend()
419
0
{
420
0
  NS_ENSURE_TRUE(mPumpingData, NS_ERROR_NOT_INITIALIZED);
421
0
  NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_IMPLEMENTED);
422
0
  return mRequest->Suspend();
423
0
}
424
425
NS_IMETHODIMP
426
nsBaseChannel::Resume()
427
0
{
428
0
  NS_ENSURE_TRUE(mPumpingData, NS_ERROR_NOT_INITIALIZED);
429
0
  NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_IMPLEMENTED);
430
0
  return mRequest->Resume();
431
0
}
432
433
NS_IMETHODIMP
434
nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
435
0
{
436
0
  *aLoadFlags = mLoadFlags;
437
0
  return NS_OK;
438
0
}
439
440
NS_IMETHODIMP
441
nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
442
0
{
443
0
  mLoadFlags = aLoadFlags;
444
0
  return NS_OK;
445
0
}
446
447
NS_IMETHODIMP
448
nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
449
0
{
450
0
  NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
451
0
  return NS_OK;
452
0
}
453
454
NS_IMETHODIMP
455
nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
456
0
{
457
0
  if (!CanSetLoadGroup(aLoadGroup)) {
458
0
    return NS_ERROR_FAILURE;
459
0
  }
460
0
461
0
  mLoadGroup = aLoadGroup;
462
0
  CallbacksChanged();
463
0
  UpdatePrivateBrowsing();
464
0
  return NS_OK;
465
0
}
466
467
//-----------------------------------------------------------------------------
468
// nsBaseChannel::nsIChannel
469
470
NS_IMETHODIMP
471
nsBaseChannel::GetOriginalURI(nsIURI **aURI)
472
0
{
473
0
  *aURI = OriginalURI();
474
0
  NS_ADDREF(*aURI);
475
0
  return NS_OK;
476
0
}
477
478
NS_IMETHODIMP
479
nsBaseChannel::SetOriginalURI(nsIURI *aURI)
480
0
{
481
0
  NS_ENSURE_ARG_POINTER(aURI);
482
0
  mOriginalURI = aURI;
483
0
  return NS_OK;
484
0
}
485
486
NS_IMETHODIMP
487
nsBaseChannel::GetURI(nsIURI **aURI)
488
0
{
489
0
  NS_IF_ADDREF(*aURI = mURI);
490
0
  return NS_OK;
491
0
}
492
493
NS_IMETHODIMP
494
nsBaseChannel::GetOwner(nsISupports **aOwner)
495
0
{
496
0
  NS_IF_ADDREF(*aOwner = mOwner);
497
0
  return NS_OK;
498
0
}
499
500
NS_IMETHODIMP
501
nsBaseChannel::SetOwner(nsISupports *aOwner)
502
0
{
503
0
  mOwner = aOwner;
504
0
  return NS_OK;
505
0
}
506
507
NS_IMETHODIMP
508
nsBaseChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
509
0
{
510
0
  mLoadInfo = aLoadInfo;
511
0
512
0
  // Need to update |mNeckoTarget| when load info has changed.
513
0
  SetupNeckoTarget();
514
0
  return NS_OK;
515
0
}
516
517
NS_IMETHODIMP
518
nsBaseChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
519
0
{
520
0
  NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
521
0
  return NS_OK;
522
0
}
523
524
NS_IMETHODIMP
525
nsBaseChannel::GetIsDocument(bool *aIsDocument)
526
0
{
527
0
  return NS_GetIsDocumentChannel(this, aIsDocument);
528
0
}
529
530
NS_IMETHODIMP
531
nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
532
0
{
533
0
  NS_IF_ADDREF(*aCallbacks = mCallbacks);
534
0
  return NS_OK;
535
0
}
536
537
NS_IMETHODIMP
538
nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
539
0
{
540
0
  if (!CanSetCallbacks(aCallbacks)) {
541
0
    return NS_ERROR_FAILURE;
542
0
  }
543
0
544
0
  mCallbacks = aCallbacks;
545
0
  CallbacksChanged();
546
0
  UpdatePrivateBrowsing();
547
0
  return NS_OK;
548
0
}
549
550
NS_IMETHODIMP
551
nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
552
0
{
553
0
  NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
554
0
  return NS_OK;
555
0
}
556
557
NS_IMETHODIMP
558
nsBaseChannel::GetContentType(nsACString &aContentType)
559
0
{
560
0
  aContentType = mContentType;
561
0
  return NS_OK;
562
0
}
563
564
NS_IMETHODIMP
565
nsBaseChannel::SetContentType(const nsACString &aContentType)
566
0
{
567
0
  // mContentCharset is unchanged if not parsed
568
0
  bool dummy;
569
0
  net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
570
0
  return NS_OK;
571
0
}
572
573
NS_IMETHODIMP
574
nsBaseChannel::GetContentCharset(nsACString &aContentCharset)
575
0
{
576
0
  aContentCharset = mContentCharset;
577
0
  return NS_OK;
578
0
}
579
580
NS_IMETHODIMP
581
nsBaseChannel::SetContentCharset(const nsACString &aContentCharset)
582
0
{
583
0
  mContentCharset = aContentCharset;
584
0
  return NS_OK;
585
0
}
586
587
NS_IMETHODIMP
588
nsBaseChannel::GetContentDisposition(uint32_t *aContentDisposition)
589
0
{
590
0
  // preserve old behavior, fail unless explicitly set.
591
0
  if (mContentDispositionHint == UINT32_MAX) {
592
0
    return NS_ERROR_NOT_AVAILABLE;
593
0
  }
594
0
595
0
  *aContentDisposition = mContentDispositionHint;
596
0
  return NS_OK;
597
0
}
598
599
NS_IMETHODIMP
600
nsBaseChannel::SetContentDisposition(uint32_t aContentDisposition)
601
0
{
602
0
  mContentDispositionHint = aContentDisposition;
603
0
  return NS_OK;
604
0
}
605
606
NS_IMETHODIMP
607
nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
608
0
{
609
0
  if (!mContentDispositionFilename) {
610
0
    return NS_ERROR_NOT_AVAILABLE;
611
0
  }
612
0
613
0
  aContentDispositionFilename = *mContentDispositionFilename;
614
0
  return NS_OK;
615
0
}
616
617
NS_IMETHODIMP
618
nsBaseChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
619
0
{
620
0
  mContentDispositionFilename = new nsString(aContentDispositionFilename);
621
0
  return NS_OK;
622
0
}
623
624
NS_IMETHODIMP
625
nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
626
0
{
627
0
  return NS_ERROR_NOT_AVAILABLE;
628
0
}
629
630
NS_IMETHODIMP
631
nsBaseChannel::GetContentLength(int64_t *aContentLength)
632
0
{
633
0
  *aContentLength = mContentLength;
634
0
  return NS_OK;
635
0
}
636
637
NS_IMETHODIMP
638
nsBaseChannel::SetContentLength(int64_t aContentLength)
639
0
{
640
0
  mContentLength = aContentLength;
641
0
  return NS_OK;
642
0
}
643
644
NS_IMETHODIMP
645
nsBaseChannel::Open(nsIInputStream **result)
646
0
{
647
0
  NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
648
0
  NS_ENSURE_TRUE(!mPumpingData, NS_ERROR_IN_PROGRESS);
649
0
  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
650
0
651
0
  nsCOMPtr<nsIChannel> chan;
652
0
  nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan));
653
0
  NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?");
654
0
  if (NS_SUCCEEDED(rv) && chan) {
655
0
      rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false);
656
0
      if (NS_FAILED(rv))
657
0
          return rv;
658
0
      rv = chan->Open(result);
659
0
  } else if (rv == NS_ERROR_NOT_IMPLEMENTED)
660
0
    return NS_ImplementChannelOpen(this, result);
661
0
662
0
  if (NS_SUCCEEDED(rv)) {
663
0
    mWasOpened = true;
664
0
    ClassifyURI();
665
0
  }
666
0
667
0
  return rv;
668
0
}
669
670
NS_IMETHODIMP
671
nsBaseChannel::Open2(nsIInputStream** aStream)
672
0
{
673
0
  nsCOMPtr<nsIStreamListener> listener;
674
0
  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
675
0
  NS_ENSURE_SUCCESS(rv, rv);
676
0
  return Open(aStream);
677
0
}
678
679
NS_IMETHODIMP
680
nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
681
0
{
682
0
  MOZ_ASSERT(!mLoadInfo ||
683
0
             mLoadInfo->GetSecurityMode() == 0 ||
684
0
             mLoadInfo->GetInitialSecurityCheckDone() ||
685
0
             (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
686
0
              nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
687
0
             "security flags in loadInfo but asyncOpen2() not called");
688
0
689
0
  NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
690
0
  NS_ENSURE_TRUE(!mPumpingData, NS_ERROR_IN_PROGRESS);
691
0
  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
692
0
  NS_ENSURE_ARG(listener);
693
0
694
0
  SetupNeckoTarget();
695
0
696
0
  // Skip checking for chrome:// sub-resources.
697
0
  nsAutoCString scheme;
698
0
  mURI->GetScheme(scheme);
699
0
  if (!scheme.EqualsLiteral("file")) {
700
0
    NS_CompareLoadInfoAndLoadContext(this);
701
0
  }
702
0
703
0
  // Ensure that this is an allowed port before proceeding.
704
0
  nsresult rv = NS_CheckPortSafety(mURI);
705
0
  if (NS_FAILED(rv)) {
706
0
    mCallbacks = nullptr;
707
0
    return rv;
708
0
  }
709
0
710
0
  // Store the listener and context early so that OpenContentStream and the
711
0
  // stream's AsyncWait method (called by AsyncRead) can have access to them
712
0
  // via PushStreamConverter and the StreamListener methods.  However, since
713
0
  // this typically introduces a reference cycle between this and the listener,
714
0
  // we need to be sure to break the reference if this method does not succeed.
715
0
  mListener = listener;
716
0
  mListenerContext = ctxt;
717
0
718
0
  // This method assigns mPump as a side-effect.  We need to clear mPump if
719
0
  // this method fails.
720
0
  rv = BeginPumpingData();
721
0
  if (NS_FAILED(rv)) {
722
0
    mPump = nullptr;
723
0
    mRequest = nullptr;
724
0
    mPumpingData = false;
725
0
    ChannelDone();
726
0
    mCallbacks = nullptr;
727
0
    return rv;
728
0
  }
729
0
730
0
  // At this point, we are going to return success no matter what.
731
0
732
0
  mWasOpened = true;
733
0
734
0
  SUSPEND_PUMP_FOR_SCOPE();
735
0
736
0
  if (mLoadGroup)
737
0
    mLoadGroup->AddRequest(this, nullptr);
738
0
739
0
  ClassifyURI();
740
0
741
0
  return NS_OK;
742
0
}
743
744
NS_IMETHODIMP
745
nsBaseChannel::AsyncOpen2(nsIStreamListener *aListener)
746
0
{
747
0
  nsCOMPtr<nsIStreamListener> listener = aListener;
748
0
  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
749
0
  if (NS_FAILED(rv)) {
750
0
    mCallbacks = nullptr;
751
0
    return rv;
752
0
  }
753
0
  return AsyncOpen(listener, nullptr);
754
0
}
755
756
//-----------------------------------------------------------------------------
757
// nsBaseChannel::nsITransportEventSink
758
759
NS_IMETHODIMP
760
nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status,
761
                                 int64_t progress, int64_t progressMax)
762
0
{
763
0
  // In some cases, we may wish to suppress transport-layer status events.
764
0
765
0
  if (!mPumpingData || NS_FAILED(mStatus)) {
766
0
    return NS_OK;
767
0
  }
768
0
769
0
  SUSPEND_PUMP_FOR_SCOPE();
770
0
771
0
  // Lazily fetch mProgressSink
772
0
  if (!mProgressSink) {
773
0
    if (mQueriedProgressSink) {
774
0
      return NS_OK;
775
0
    }
776
0
    GetCallback(mProgressSink);
777
0
    mQueriedProgressSink = true;
778
0
    if (!mProgressSink) {
779
0
      return NS_OK;
780
0
    }
781
0
  }
782
0
783
0
  if (!HasLoadFlag(LOAD_BACKGROUND)) {
784
0
    nsAutoString statusArg;
785
0
    if (GetStatusArg(status, statusArg)) {
786
0
      mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get());
787
0
    }
788
0
  }
789
0
790
0
  if (progress) {
791
0
    mProgressSink->OnProgress(this, mListenerContext, progress, progressMax);
792
0
  }
793
0
794
0
  return NS_OK;
795
0
}
796
797
//-----------------------------------------------------------------------------
798
// nsBaseChannel::nsIInterfaceRequestor
799
800
NS_IMETHODIMP
801
nsBaseChannel::GetInterface(const nsIID &iid, void **result)
802
0
{
803
0
  NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result);
804
0
  return *result ? NS_OK : NS_ERROR_NO_INTERFACE;
805
0
}
806
807
//-----------------------------------------------------------------------------
808
// nsBaseChannel::nsIRequestObserver
809
810
static void
811
CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount)
812
0
{
813
0
  nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
814
0
815
0
  nsAutoCString newType;
816
0
  NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType);
817
0
  if (!newType.IsEmpty()) {
818
0
    chan->SetContentType(newType);
819
0
  }
820
0
}
821
822
static void
823
CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount)
824
0
{
825
0
  nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
826
0
827
0
  RefPtr<nsUnknownDecoder> sniffer = new nsUnknownDecoder();
828
0
829
0
  nsAutoCString detected;
830
0
  nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected);
831
0
  if (NS_SUCCEEDED(rv))
832
0
    chan->SetContentType(detected);
833
0
}
834
835
NS_IMETHODIMP
836
nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
837
0
{
838
0
  MOZ_ASSERT_IF(mRequest, request == mRequest);
839
0
840
0
  if (mPump) {
841
0
    // If our content type is unknown, use the content type
842
0
    // sniffer. If the sniffer is not available for some reason, then we just keep
843
0
    // going as-is.
844
0
    if (NS_SUCCEEDED(mStatus) &&
845
0
        mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
846
0
      mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this));
847
0
    }
848
0
849
0
    // Now, the general type sniffers. Skip this if we have none.
850
0
    if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS)
851
0
      mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
852
0
  }
853
0
854
0
  SUSPEND_PUMP_FOR_SCOPE();
855
0
856
0
  if (mListener) // null in case of redirect
857
0
      return mListener->OnStartRequest(this, mListenerContext);
858
0
  return NS_OK;
859
0
}
860
861
NS_IMETHODIMP
862
nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
863
                             nsresult status)
864
0
{
865
0
  // If both mStatus and status are failure codes, we keep mStatus as-is since
866
0
  // that is consistent with our GetStatus and Cancel methods.
867
0
  if (NS_SUCCEEDED(mStatus))
868
0
    mStatus = status;
869
0
870
0
  // Cause Pending to return false.
871
0
  mPump = nullptr;
872
0
  mRequest = nullptr;
873
0
  mPumpingData = false;
874
0
875
0
  if (mListener) // null in case of redirect
876
0
      mListener->OnStopRequest(this, mListenerContext, mStatus);
877
0
  ChannelDone();
878
0
879
0
  // No need to suspend pump in this scope since we will not be receiving
880
0
  // any more events from it.
881
0
882
0
  if (mLoadGroup)
883
0
    mLoadGroup->RemoveRequest(this, nullptr, mStatus);
884
0
885
0
  // Drop notification callbacks to prevent cycles.
886
0
  mCallbacks = nullptr;
887
0
  CallbacksChanged();
888
0
889
0
  return NS_OK;
890
0
}
891
892
//-----------------------------------------------------------------------------
893
// nsBaseChannel::nsIStreamListener
894
895
NS_IMETHODIMP
896
nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
897
                               nsIInputStream *stream, uint64_t offset,
898
                               uint32_t count)
899
0
{
900
0
  SUSPEND_PUMP_FOR_SCOPE();
901
0
902
0
  nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream,
903
0
                                           offset, count);
904
0
  if (mSynthProgressEvents && NS_SUCCEEDED(rv)) {
905
0
    int64_t prog = offset + count;
906
0
    if (NS_IsMainThread()) {
907
0
      OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength);
908
0
    } else {
909
0
      class OnTransportStatusAsyncEvent : public mozilla::Runnable
910
0
      {
911
0
        RefPtr<nsBaseChannel> mChannel;
912
0
        int64_t mProgress;
913
0
        int64_t mContentLength;
914
0
      public:
915
0
        OnTransportStatusAsyncEvent(nsBaseChannel* aChannel,
916
0
                                    int64_t aProgress,
917
0
                                    int64_t aContentLength)
918
0
          : mozilla::Runnable("OnTransportStatusAsyncEvent")
919
0
          , mChannel(aChannel)
920
0
          , mProgress(aProgress)
921
0
          , mContentLength(aContentLength)
922
0
        { }
923
0
924
0
        NS_IMETHOD Run() override
925
0
        {
926
0
          return mChannel->OnTransportStatus(nullptr, NS_NET_STATUS_READING,
927
0
                                             mProgress, mContentLength);
928
0
        }
929
0
      };
930
0
931
0
      nsCOMPtr<nsIRunnable> runnable =
932
0
        new OnTransportStatusAsyncEvent(this, prog, mContentLength);
933
0
      Dispatch(runnable.forget());
934
0
    }
935
0
  }
936
0
937
0
  return rv;
938
0
}
939
940
NS_IMETHODIMP
941
nsBaseChannel::OnRedirectVerifyCallback(nsresult result)
942
0
{
943
0
  if (NS_SUCCEEDED(result))
944
0
    result = ContinueRedirect();
945
0
946
0
  if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) {
947
0
    if (NS_SUCCEEDED(mStatus))
948
0
      mStatus = result;
949
0
    return NS_OK;
950
0
  }
951
0
952
0
  if (mWaitingOnAsyncRedirect)
953
0
    ContinueHandleAsyncRedirect(result);
954
0
955
0
  return NS_OK;
956
0
}
957
958
NS_IMETHODIMP
959
nsBaseChannel::RetargetDeliveryTo(nsIEventTarget* aEventTarget)
960
0
{
961
0
  MOZ_ASSERT(NS_IsMainThread());
962
0
963
0
  NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_INITIALIZED);
964
0
965
0
  nsCOMPtr<nsIThreadRetargetableRequest> req;
966
0
  if (mAllowThreadRetargeting) {
967
0
    req = do_QueryInterface(mRequest);
968
0
  }
969
0
970
0
  NS_ENSURE_TRUE(req, NS_ERROR_NOT_IMPLEMENTED);
971
0
972
0
  return req->RetargetDeliveryTo(aEventTarget);
973
0
}
974
975
NS_IMETHODIMP
976
nsBaseChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget)
977
0
{
978
0
  MOZ_ASSERT(NS_IsMainThread());
979
0
980
0
  NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_INITIALIZED);
981
0
982
0
  nsCOMPtr<nsIThreadRetargetableRequest> req;
983
0
    req = do_QueryInterface(mRequest);
984
0
985
0
  NS_ENSURE_TRUE(req, NS_ERROR_NOT_IMPLEMENTED);
986
0
  return req->GetDeliveryTarget(aEventTarget);
987
0
}
988
989
NS_IMETHODIMP
990
nsBaseChannel::CheckListenerChain()
991
0
{
992
0
  MOZ_ASSERT(NS_IsMainThread());
993
0
994
0
  if (!mAllowThreadRetargeting) {
995
0
    return NS_ERROR_NOT_IMPLEMENTED;
996
0
  }
997
0
998
0
  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
999
0
    do_QueryInterface(mListener);
1000
0
  if (!listener) {
1001
0
    return NS_ERROR_NO_INTERFACE;
1002
0
  }
1003
0
1004
0
  return listener->CheckListenerChain();
1005
0
}
1006
1007
void
1008
nsBaseChannel::SetupNeckoTarget()
1009
0
{
1010
0
  mNeckoTarget =
1011
0
    nsContentUtils::GetEventTargetByLoadInfo(mLoadInfo, TaskCategory::Other);
1012
0
}