Source code

Revision control

Other Tools

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