netwerk/base/src/nsBaseChannel.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:8c9854a2cb29
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsBaseChannel.h"
7 #include "nsURLHelper.h"
8 #include "nsNetUtil.h"
9 #include "nsMimeTypes.h"
10 #include "nsIHttpEventSink.h"
11 #include "nsIHttpChannel.h"
12 #include "nsIChannelEventSink.h"
13 #include "nsIStreamConverterService.h"
14 #include "nsChannelClassifier.h"
15 #include "nsAsyncRedirectVerifyHelper.h"
16
17 static PLDHashOperator
18 CopyProperties(const nsAString &key, nsIVariant *data, void *closure)
19 {
20 nsIWritablePropertyBag *bag =
21 static_cast<nsIWritablePropertyBag *>(closure);
22
23 bag->SetProperty(key, data);
24 return PL_DHASH_NEXT;
25 }
26
27 // This class is used to suspend a request across a function scope.
28 class ScopedRequestSuspender {
29 public:
30 ScopedRequestSuspender(nsIRequest *request)
31 : mRequest(request) {
32 if (mRequest && NS_FAILED(mRequest->Suspend())) {
33 NS_WARNING("Couldn't suspend pump");
34 mRequest = nullptr;
35 }
36 }
37 ~ScopedRequestSuspender() {
38 if (mRequest)
39 mRequest->Resume();
40 }
41 private:
42 nsIRequest *mRequest;
43 };
44
45 // Used to suspend data events from mPump 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 ScopedRequestSuspender pump_suspender__(mPump)
49
50 //-----------------------------------------------------------------------------
51 // nsBaseChannel
52
53 nsBaseChannel::nsBaseChannel()
54 : mLoadFlags(LOAD_NORMAL)
55 , mQueriedProgressSink(true)
56 , mSynthProgressEvents(false)
57 , mWasOpened(false)
58 , mWaitingOnAsyncRedirect(false)
59 , mStatus(NS_OK)
60 , mContentDispositionHint(UINT32_MAX)
61 , mContentLength(-1)
62 {
63 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
64 }
65
66 nsresult
67 nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags,
68 bool openNewChannel)
69 {
70 SUSPEND_PUMP_FOR_SCOPE();
71
72 // Transfer properties
73
74 newChannel->SetLoadGroup(mLoadGroup);
75 newChannel->SetNotificationCallbacks(mCallbacks);
76 newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE);
77
78 // Try to preserve the privacy bit if it has been overridden
79 if (mPrivateBrowsingOverriden) {
80 nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel =
81 do_QueryInterface(newChannel);
82 if (newPBChannel) {
83 newPBChannel->SetPrivate(mPrivateBrowsing);
84 }
85 }
86
87 nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel);
88 if (bag)
89 mPropertyHash.EnumerateRead(CopyProperties, bag.get());
90
91 // Notify consumer, giving chance to cancel redirect. For backwards compat,
92 // we support nsIHttpEventSink if we are an HTTP channel and if this is not
93 // an internal redirect.
94
95 nsRefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
96 new nsAsyncRedirectVerifyHelper();
97
98 bool checkRedirectSynchronously = !openNewChannel;
99
100 mRedirectChannel = newChannel;
101 mRedirectFlags = redirectFlags;
102 mOpenRedirectChannel = openNewChannel;
103 nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags,
104 checkRedirectSynchronously);
105 if (NS_FAILED(rv))
106 return rv;
107
108 if (checkRedirectSynchronously && NS_FAILED(mStatus))
109 return mStatus;
110
111 return NS_OK;
112 }
113
114 nsresult
115 nsBaseChannel::ContinueRedirect()
116 {
117 // Backwards compat for non-internal redirects from a HTTP channel.
118 // XXX Is our http channel implementation going to derive from nsBaseChannel?
119 // If not, this code can be removed.
120 if (!(mRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
121 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface();
122 if (httpChannel) {
123 nsCOMPtr<nsIHttpEventSink> httpEventSink;
124 GetCallback(httpEventSink);
125 if (httpEventSink) {
126 nsresult rv = httpEventSink->OnRedirect(httpChannel, mRedirectChannel);
127 if (NS_FAILED(rv)) {
128 return rv;
129 }
130 }
131 }
132 }
133
134 // Make sure to do this _after_ making all the OnChannelRedirect calls
135 mRedirectChannel->SetOriginalURI(OriginalURI());
136
137 // If we fail to open the new channel, then we want to leave this channel
138 // unaffected, so we defer tearing down our channel until we have succeeded
139 // with the redirect.
140
141 if (mOpenRedirectChannel) {
142 nsresult rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
143 if (NS_FAILED(rv))
144 return rv;
145 }
146
147 mRedirectChannel = nullptr;
148
149 // close down this channel
150 Cancel(NS_BINDING_REDIRECTED);
151 ChannelDone();
152
153 return NS_OK;
154 }
155
156 bool
157 nsBaseChannel::HasContentTypeHint() const
158 {
159 NS_ASSERTION(!Pending(), "HasContentTypeHint called too late");
160 return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE);
161 }
162
163 nsresult
164 nsBaseChannel::PushStreamConverter(const char *fromType,
165 const char *toType,
166 bool invalidatesContentLength,
167 nsIStreamListener **result)
168 {
169 NS_ASSERTION(mListener, "no listener");
170
171 nsresult rv;
172 nsCOMPtr<nsIStreamConverterService> scs =
173 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
174 if (NS_FAILED(rv))
175 return rv;
176
177 nsCOMPtr<nsIStreamListener> converter;
178 rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext,
179 getter_AddRefs(converter));
180 if (NS_SUCCEEDED(rv)) {
181 mListener = converter;
182 if (invalidatesContentLength)
183 mContentLength = -1;
184 if (result) {
185 *result = nullptr;
186 converter.swap(*result);
187 }
188 }
189 return rv;
190 }
191
192 nsresult
193 nsBaseChannel::BeginPumpingData()
194 {
195 nsCOMPtr<nsIInputStream> stream;
196 nsCOMPtr<nsIChannel> channel;
197 nsresult rv = OpenContentStream(true, getter_AddRefs(stream),
198 getter_AddRefs(channel));
199 if (NS_FAILED(rv))
200 return rv;
201
202 NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?");
203
204 if (channel) {
205 rv = NS_DispatchToCurrentThread(new RedirectRunnable(this, channel));
206 if (NS_SUCCEEDED(rv))
207 mWaitingOnAsyncRedirect = true;
208 return rv;
209 }
210
211 // By assigning mPump, we flag this channel as pending (see Pending). It's
212 // important that the pending flag is set when we call into the stream (the
213 // call to AsyncRead results in the stream's AsyncWait method being called)
214 // and especially when we call into the loadgroup. Our caller takes care to
215 // release mPump if we return an error.
216
217 rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0,
218 true);
219 if (NS_SUCCEEDED(rv))
220 rv = mPump->AsyncRead(this, nullptr);
221
222 return rv;
223 }
224
225 void
226 nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
227 {
228 NS_ASSERTION(!mPump, "Shouldn't have gotten here");
229
230 nsresult rv = mStatus;
231 if (NS_SUCCEEDED(mStatus)) {
232 rv = Redirect(newChannel,
233 nsIChannelEventSink::REDIRECT_TEMPORARY,
234 true);
235 if (NS_SUCCEEDED(rv)) {
236 // OnRedirectVerifyCallback will be called asynchronously
237 return;
238 }
239 }
240
241 ContinueHandleAsyncRedirect(rv);
242 }
243
244 void
245 nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result)
246 {
247 mWaitingOnAsyncRedirect = false;
248
249 if (NS_FAILED(result))
250 Cancel(result);
251
252 if (NS_FAILED(result) && mListener) {
253 // Notify our consumer ourselves
254 mListener->OnStartRequest(this, mListenerContext);
255 mListener->OnStopRequest(this, mListenerContext, mStatus);
256 ChannelDone();
257 }
258
259 if (mLoadGroup)
260 mLoadGroup->RemoveRequest(this, nullptr, mStatus);
261
262 // Drop notification callbacks to prevent cycles.
263 mCallbacks = nullptr;
264 CallbacksChanged();
265 }
266
267 void
268 nsBaseChannel::ClassifyURI()
269 {
270 nsresult rv;
271
272 if (mLoadFlags & LOAD_CLASSIFY_URI) {
273 nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
274 if (classifier) {
275 rv = classifier->Start(this);
276 if (NS_FAILED(rv)) {
277 Cancel(rv);
278 }
279 } else {
280 Cancel(NS_ERROR_OUT_OF_MEMORY);
281 }
282 }
283 }
284
285 //-----------------------------------------------------------------------------
286 // nsBaseChannel::nsISupports
287
288 NS_IMPL_ISUPPORTS_INHERITED(nsBaseChannel,
289 nsHashPropertyBag,
290 nsIRequest,
291 nsIChannel,
292 nsIInterfaceRequestor,
293 nsITransportEventSink,
294 nsIRequestObserver,
295 nsIStreamListener,
296 nsIAsyncVerifyRedirectCallback,
297 nsIPrivateBrowsingChannel)
298
299 //-----------------------------------------------------------------------------
300 // nsBaseChannel::nsIRequest
301
302 NS_IMETHODIMP
303 nsBaseChannel::GetName(nsACString &result)
304 {
305 if (!mURI) {
306 result.Truncate();
307 return NS_OK;
308 }
309 return mURI->GetSpec(result);
310 }
311
312 NS_IMETHODIMP
313 nsBaseChannel::IsPending(bool *result)
314 {
315 *result = Pending();
316 return NS_OK;
317 }
318
319 NS_IMETHODIMP
320 nsBaseChannel::GetStatus(nsresult *status)
321 {
322 if (mPump && NS_SUCCEEDED(mStatus)) {
323 mPump->GetStatus(status);
324 } else {
325 *status = mStatus;
326 }
327 return NS_OK;
328 }
329
330 NS_IMETHODIMP
331 nsBaseChannel::Cancel(nsresult status)
332 {
333 // Ignore redundant cancelation
334 if (NS_FAILED(mStatus))
335 return NS_OK;
336
337 mStatus = status;
338
339 if (mPump)
340 mPump->Cancel(status);
341
342 return NS_OK;
343 }
344
345 NS_IMETHODIMP
346 nsBaseChannel::Suspend()
347 {
348 NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
349 return mPump->Suspend();
350 }
351
352 NS_IMETHODIMP
353 nsBaseChannel::Resume()
354 {
355 NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
356 return mPump->Resume();
357 }
358
359 NS_IMETHODIMP
360 nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
361 {
362 *aLoadFlags = mLoadFlags;
363 return NS_OK;
364 }
365
366 NS_IMETHODIMP
367 nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
368 {
369 mLoadFlags = aLoadFlags;
370 return NS_OK;
371 }
372
373 NS_IMETHODIMP
374 nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
375 {
376 NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
377 return NS_OK;
378 }
379
380 NS_IMETHODIMP
381 nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
382 {
383 if (!CanSetLoadGroup(aLoadGroup)) {
384 return NS_ERROR_FAILURE;
385 }
386
387 mLoadGroup = aLoadGroup;
388 CallbacksChanged();
389 return NS_OK;
390 }
391
392 //-----------------------------------------------------------------------------
393 // nsBaseChannel::nsIChannel
394
395 NS_IMETHODIMP
396 nsBaseChannel::GetOriginalURI(nsIURI **aURI)
397 {
398 *aURI = OriginalURI();
399 NS_ADDREF(*aURI);
400 return NS_OK;
401 }
402
403 NS_IMETHODIMP
404 nsBaseChannel::SetOriginalURI(nsIURI *aURI)
405 {
406 NS_ENSURE_ARG_POINTER(aURI);
407 mOriginalURI = aURI;
408 return NS_OK;
409 }
410
411 NS_IMETHODIMP
412 nsBaseChannel::GetURI(nsIURI **aURI)
413 {
414 NS_IF_ADDREF(*aURI = mURI);
415 return NS_OK;
416 }
417
418 NS_IMETHODIMP
419 nsBaseChannel::GetOwner(nsISupports **aOwner)
420 {
421 NS_IF_ADDREF(*aOwner = mOwner);
422 return NS_OK;
423 }
424
425 NS_IMETHODIMP
426 nsBaseChannel::SetOwner(nsISupports *aOwner)
427 {
428 mOwner = aOwner;
429 return NS_OK;
430 }
431
432 NS_IMETHODIMP
433 nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
434 {
435 NS_IF_ADDREF(*aCallbacks = mCallbacks);
436 return NS_OK;
437 }
438
439 NS_IMETHODIMP
440 nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
441 {
442 if (!CanSetCallbacks(aCallbacks)) {
443 return NS_ERROR_FAILURE;
444 }
445
446 mCallbacks = aCallbacks;
447 CallbacksChanged();
448 return NS_OK;
449 }
450
451 NS_IMETHODIMP
452 nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
453 {
454 NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
455 return NS_OK;
456 }
457
458 NS_IMETHODIMP
459 nsBaseChannel::GetContentType(nsACString &aContentType)
460 {
461 aContentType = mContentType;
462 return NS_OK;
463 }
464
465 NS_IMETHODIMP
466 nsBaseChannel::SetContentType(const nsACString &aContentType)
467 {
468 // mContentCharset is unchanged if not parsed
469 bool dummy;
470 net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
471 return NS_OK;
472 }
473
474 NS_IMETHODIMP
475 nsBaseChannel::GetContentCharset(nsACString &aContentCharset)
476 {
477 aContentCharset = mContentCharset;
478 return NS_OK;
479 }
480
481 NS_IMETHODIMP
482 nsBaseChannel::SetContentCharset(const nsACString &aContentCharset)
483 {
484 mContentCharset = aContentCharset;
485 return NS_OK;
486 }
487
488 NS_IMETHODIMP
489 nsBaseChannel::GetContentDisposition(uint32_t *aContentDisposition)
490 {
491 // preserve old behavior, fail unless explicitly set.
492 if (mContentDispositionHint == UINT32_MAX) {
493 return NS_ERROR_NOT_AVAILABLE;
494 }
495
496 *aContentDisposition = mContentDispositionHint;
497 return NS_OK;
498 }
499
500 NS_IMETHODIMP
501 nsBaseChannel::SetContentDisposition(uint32_t aContentDisposition)
502 {
503 mContentDispositionHint = aContentDisposition;
504 return NS_OK;
505 }
506
507 NS_IMETHODIMP
508 nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
509 {
510 if (!mContentDispositionFilename) {
511 return NS_ERROR_NOT_AVAILABLE;
512 }
513
514 aContentDispositionFilename = *mContentDispositionFilename;
515 return NS_OK;
516 }
517
518 NS_IMETHODIMP
519 nsBaseChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
520 {
521 mContentDispositionFilename = new nsString(aContentDispositionFilename);
522 return NS_OK;
523 }
524
525 NS_IMETHODIMP
526 nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
527 {
528 return NS_ERROR_NOT_AVAILABLE;
529 }
530
531 NS_IMETHODIMP
532 nsBaseChannel::GetContentLength(int64_t *aContentLength)
533 {
534 *aContentLength = mContentLength;
535 return NS_OK;
536 }
537
538 NS_IMETHODIMP
539 nsBaseChannel::SetContentLength(int64_t aContentLength)
540 {
541 mContentLength = aContentLength;
542 return NS_OK;
543 }
544
545 NS_IMETHODIMP
546 nsBaseChannel::Open(nsIInputStream **result)
547 {
548 NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
549 NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
550 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
551
552 nsCOMPtr<nsIChannel> chan;
553 nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan));
554 NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?");
555 if (NS_SUCCEEDED(rv) && chan) {
556 rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false);
557 if (NS_FAILED(rv))
558 return rv;
559 rv = chan->Open(result);
560 } else if (rv == NS_ERROR_NOT_IMPLEMENTED)
561 return NS_ImplementChannelOpen(this, result);
562
563 if (NS_SUCCEEDED(rv)) {
564 mWasOpened = true;
565 ClassifyURI();
566 }
567
568 return rv;
569 }
570
571 NS_IMETHODIMP
572 nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
573 {
574 NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
575 NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
576 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
577 NS_ENSURE_ARG(listener);
578
579 // Ensure that this is an allowed port before proceeding.
580 nsresult rv = NS_CheckPortSafety(mURI);
581 if (NS_FAILED(rv)) {
582 mCallbacks = nullptr;
583 return rv;
584 }
585
586 // Store the listener and context early so that OpenContentStream and the
587 // stream's AsyncWait method (called by AsyncRead) can have access to them
588 // via PushStreamConverter and the StreamListener methods. However, since
589 // this typically introduces a reference cycle between this and the listener,
590 // we need to be sure to break the reference if this method does not succeed.
591 mListener = listener;
592 mListenerContext = ctxt;
593
594 // This method assigns mPump as a side-effect. We need to clear mPump if
595 // this method fails.
596 rv = BeginPumpingData();
597 if (NS_FAILED(rv)) {
598 mPump = nullptr;
599 ChannelDone();
600 mCallbacks = nullptr;
601 return rv;
602 }
603
604 // At this point, we are going to return success no matter what.
605
606 mWasOpened = true;
607
608 SUSPEND_PUMP_FOR_SCOPE();
609
610 if (mLoadGroup)
611 mLoadGroup->AddRequest(this, nullptr);
612
613 ClassifyURI();
614
615 return NS_OK;
616 }
617
618 //-----------------------------------------------------------------------------
619 // nsBaseChannel::nsITransportEventSink
620
621 NS_IMETHODIMP
622 nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status,
623 uint64_t progress, uint64_t progressMax)
624 {
625 // In some cases, we may wish to suppress transport-layer status events.
626
627 if (!mPump || NS_FAILED(mStatus) || HasLoadFlag(LOAD_BACKGROUND))
628 return NS_OK;
629
630 SUSPEND_PUMP_FOR_SCOPE();
631
632 // Lazily fetch mProgressSink
633 if (!mProgressSink) {
634 if (mQueriedProgressSink)
635 return NS_OK;
636 GetCallback(mProgressSink);
637 mQueriedProgressSink = true;
638 if (!mProgressSink)
639 return NS_OK;
640 }
641
642 nsAutoString statusArg;
643 if (GetStatusArg(status, statusArg))
644 mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get());
645
646 if (progress)
647 mProgressSink->OnProgress(this, mListenerContext, progress, progressMax);
648
649 return NS_OK;
650 }
651
652 //-----------------------------------------------------------------------------
653 // nsBaseChannel::nsIInterfaceRequestor
654
655 NS_IMETHODIMP
656 nsBaseChannel::GetInterface(const nsIID &iid, void **result)
657 {
658 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result);
659 return *result ? NS_OK : NS_ERROR_NO_INTERFACE;
660 }
661
662 //-----------------------------------------------------------------------------
663 // nsBaseChannel::nsIRequestObserver
664
665 static void
666 CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount)
667 {
668 nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
669
670 nsAutoCString newType;
671 NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType);
672 if (!newType.IsEmpty()) {
673 chan->SetContentType(newType);
674 }
675 }
676
677 static void
678 CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount)
679 {
680 nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
681
682 nsCOMPtr<nsIContentSniffer> sniffer =
683 do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER);
684 if (!sniffer)
685 return;
686
687 nsAutoCString detected;
688 nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected);
689 if (NS_SUCCEEDED(rv))
690 chan->SetContentType(detected);
691 }
692
693 NS_IMETHODIMP
694 nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
695 {
696 // If our content type is unknown or if the content type is
697 // application/octet-stream and the caller requested it, use the content type
698 // sniffer. If the sniffer is not available for some reason, then we just keep
699 // going as-is.
700 bool shouldSniff = mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
701 ((mLoadFlags & LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN) &&
702 mContentType.EqualsLiteral(APPLICATION_OCTET_STREAM));
703
704 if (NS_SUCCEEDED(mStatus) && shouldSniff) {
705 mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this));
706 }
707
708 // Now, the general type sniffers. Skip this if we have none.
709 if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS)
710 mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
711
712 SUSPEND_PUMP_FOR_SCOPE();
713
714 if (mListener) // null in case of redirect
715 return mListener->OnStartRequest(this, mListenerContext);
716 return NS_OK;
717 }
718
719 NS_IMETHODIMP
720 nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
721 nsresult status)
722 {
723 // If both mStatus and status are failure codes, we keep mStatus as-is since
724 // that is consistent with our GetStatus and Cancel methods.
725 if (NS_SUCCEEDED(mStatus))
726 mStatus = status;
727
728 // Cause Pending to return false.
729 mPump = nullptr;
730
731 if (mListener) // null in case of redirect
732 mListener->OnStopRequest(this, mListenerContext, mStatus);
733 ChannelDone();
734
735 // No need to suspend pump in this scope since we will not be receiving
736 // any more events from it.
737
738 if (mLoadGroup)
739 mLoadGroup->RemoveRequest(this, nullptr, mStatus);
740
741 // Drop notification callbacks to prevent cycles.
742 mCallbacks = nullptr;
743 CallbacksChanged();
744
745 return NS_OK;
746 }
747
748 //-----------------------------------------------------------------------------
749 // nsBaseChannel::nsIStreamListener
750
751 NS_IMETHODIMP
752 nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
753 nsIInputStream *stream, uint64_t offset,
754 uint32_t count)
755 {
756 SUSPEND_PUMP_FOR_SCOPE();
757
758 nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream,
759 offset, count);
760 if (mSynthProgressEvents && NS_SUCCEEDED(rv)) {
761 uint64_t prog = offset + count;
762 OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength);
763 }
764
765 return rv;
766 }
767
768 NS_IMETHODIMP
769 nsBaseChannel::OnRedirectVerifyCallback(nsresult result)
770 {
771 if (NS_SUCCEEDED(result))
772 result = ContinueRedirect();
773
774 if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) {
775 if (NS_SUCCEEDED(mStatus))
776 mStatus = result;
777 return NS_OK;
778 }
779
780 if (mWaitingOnAsyncRedirect)
781 ContinueHandleAsyncRedirect(result);
782
783 return NS_OK;
784 }

mercurial