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