Bug 458256. Use LoadLibraryW instead of LoadLibrary (patch by DougT). r+sr=vlad
[wine-gecko.git] / netwerk / base / src / nsBaseChannel.cpp
blobdf453e91dfe06655ba8a3811eb5fc7f01210f3aa
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
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/
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.
15 * The Original Code is mozilla.org code.
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.
21 * Contributor(s):
22 * Darin Fisher <darin@meer.net>
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.
36 * ***** END LICENSE BLOCK ***** */
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"
50 static PLDHashOperator
51 CopyProperties(const nsAString &key, nsIVariant *data, void *closure)
53 nsIWritablePropertyBag *bag =
54 static_cast<nsIWritablePropertyBag *>(closure);
56 bag->SetProperty(key, data);
57 return PL_DHASH_NEXT;
60 // This class is used to suspend a request across a function scope.
61 class ScopedRequestSuspender {
62 public:
63 ScopedRequestSuspender(nsIRequest *request)
64 : mRequest(request) {
65 if (mRequest && NS_FAILED(mRequest->Suspend())) {
66 NS_WARNING("Couldn't suspend pump");
67 mRequest = nsnull;
70 ~ScopedRequestSuspender() {
71 if (mRequest)
72 mRequest->Resume();
74 private:
75 nsIRequest *mRequest;
78 // Used to suspend data events from mPump within a function scope. This is
79 // usually needed when a function makes callbacks that could process events.
80 #define SUSPEND_PUMP_FOR_SCOPE() \
81 ScopedRequestSuspender pump_suspender__(mPump)
83 //-----------------------------------------------------------------------------
84 // nsBaseChannel
86 nsBaseChannel::nsBaseChannel()
87 : mLoadFlags(LOAD_NORMAL)
88 , mStatus(NS_OK)
89 , mQueriedProgressSink(PR_TRUE)
90 , mSynthProgressEvents(PR_FALSE)
91 , mWasOpened(PR_FALSE)
93 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
96 nsresult
97 nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags,
98 PRBool openNewChannel)
100 SUSPEND_PUMP_FOR_SCOPE();
102 // Transfer properties
104 newChannel->SetOriginalURI(OriginalURI());
105 newChannel->SetLoadGroup(mLoadGroup);
106 newChannel->SetNotificationCallbacks(mCallbacks);
107 newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE);
109 nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel);
110 if (bag)
111 mPropertyHash.EnumerateRead(CopyProperties, bag.get());
113 // Notify consumer, giving chance to cancel redirect. For backwards compat,
114 // we support nsIHttpEventSink if we are an HTTP channel and if this is not
115 // an internal redirect.
117 // Global observers. These come first so that other observers don't see
118 // redirects that get aborted for security reasons anyway.
119 NS_ASSERTION(gIOService, "Must have an IO service");
120 nsresult rv = gIOService->OnChannelRedirect(this, newChannel, redirectFlags);
121 if (NS_FAILED(rv))
122 return rv;
124 // Backwards compat for non-internal redirects from a HTTP channel.
125 if (!(redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
126 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface();
127 if (httpChannel) {
128 nsCOMPtr<nsIHttpEventSink> httpEventSink;
129 GetCallback(httpEventSink);
130 if (httpEventSink) {
131 rv = httpEventSink->OnRedirect(httpChannel, newChannel);
132 if (NS_FAILED(rv))
133 return rv;
138 nsCOMPtr<nsIChannelEventSink> channelEventSink;
139 // Give our consumer a chance to observe/block this redirect.
140 GetCallback(channelEventSink);
141 if (channelEventSink) {
142 rv = channelEventSink->OnChannelRedirect(this, newChannel, redirectFlags);
143 if (NS_FAILED(rv))
144 return rv;
147 // If we fail to open the new channel, then we want to leave this channel
148 // unaffected, so we defer tearing down our channel until we have succeeded
149 // with the redirect.
151 if (openNewChannel) {
152 rv = newChannel->AsyncOpen(mListener, mListenerContext);
153 if (NS_FAILED(rv))
154 return rv;
157 // close down this channel
158 Cancel(NS_BINDING_REDIRECTED);
159 mListener = nsnull;
160 mListenerContext = nsnull;
162 return NS_OK;
165 PRBool
166 nsBaseChannel::HasContentTypeHint() const
168 NS_ASSERTION(!IsPending(), "HasContentTypeHint called too late");
169 return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE);
172 void
173 nsBaseChannel::SetContentLength64(PRInt64 len)
175 // XXX: Storing the content-length as a property may not be what we want.
176 // It has the drawback of being copied if we redirect this channel.
177 // Maybe it is time for nsIChannel2.
178 SetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH, len);
181 PRInt64
182 nsBaseChannel::ContentLength64()
184 PRInt64 len;
185 nsresult rv = GetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH, &len);
186 return NS_SUCCEEDED(rv) ? len : -1;
189 nsresult
190 nsBaseChannel::PushStreamConverter(const char *fromType,
191 const char *toType,
192 PRBool invalidatesContentLength,
193 nsIStreamListener **result)
195 NS_ASSERTION(mListener, "no listener");
197 nsresult rv;
198 nsCOMPtr<nsIStreamConverterService> scs =
199 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
200 if (NS_FAILED(rv))
201 return rv;
203 nsCOMPtr<nsIStreamListener> converter;
204 rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext,
205 getter_AddRefs(converter));
206 if (NS_SUCCEEDED(rv)) {
207 mListener = converter;
208 if (invalidatesContentLength)
209 SetContentLength64(-1);
210 if (result) {
211 *result = nsnull;
212 converter.swap(*result);
215 return rv;
218 nsresult
219 nsBaseChannel::BeginPumpingData()
221 nsCOMPtr<nsIInputStream> stream;
222 nsCOMPtr<nsIChannel> channel;
223 nsresult rv = OpenContentStream(PR_TRUE, getter_AddRefs(stream),
224 getter_AddRefs(channel));
225 if (NS_FAILED(rv))
226 return rv;
228 NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?");
230 if (channel)
231 return NS_DispatchToCurrentThread(new RedirectRunnable(this, channel));
233 // By assigning mPump, we flag this channel as pending (see IsPending). It's
234 // important that the pending flag is set when we call into the stream (the
235 // call to AsyncRead results in the stream's AsyncWait method being called)
236 // and especially when we call into the loadgroup. Our caller takes care to
237 // release mPump if we return an error.
239 rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0,
240 PR_TRUE);
241 if (NS_SUCCEEDED(rv))
242 rv = mPump->AsyncRead(this, nsnull);
244 return rv;
247 void
248 nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
250 NS_ASSERTION(!mPump, "Shouldn't have gotten here");
251 nsresult rv = Redirect(newChannel, nsIChannelEventSink::REDIRECT_INTERNAL,
252 PR_TRUE);
253 if (NS_FAILED(rv)) {
254 // Notify our consumer ourselves
255 Cancel(rv);
256 mListener->OnStartRequest(this, mListenerContext);
257 mListener->OnStopRequest(this, mListenerContext, mStatus);
258 mListener = nsnull;
259 mListenerContext = nsnull;
262 if (mLoadGroup)
263 mLoadGroup->RemoveRequest(this, nsnull, mStatus);
265 // Drop notification callbacks to prevent cycles.
266 mCallbacks = nsnull;
267 CallbacksChanged();
270 //-----------------------------------------------------------------------------
271 // nsBaseChannel::nsISupports
273 NS_IMPL_ISUPPORTS_INHERITED6(nsBaseChannel,
274 nsHashPropertyBag,
275 nsIRequest,
276 nsIChannel,
277 nsIInterfaceRequestor,
278 nsITransportEventSink,
279 nsIRequestObserver,
280 nsIStreamListener)
282 //-----------------------------------------------------------------------------
283 // nsBaseChannel::nsIRequest
285 NS_IMETHODIMP
286 nsBaseChannel::GetName(nsACString &result)
288 if (!mURI) {
289 result.Truncate();
290 return NS_OK;
292 return mURI->GetSpec(result);
295 NS_IMETHODIMP
296 nsBaseChannel::IsPending(PRBool *result)
298 *result = IsPending();
299 return NS_OK;
302 NS_IMETHODIMP
303 nsBaseChannel::GetStatus(nsresult *status)
305 if (mPump && NS_SUCCEEDED(mStatus)) {
306 mPump->GetStatus(status);
307 } else {
308 *status = mStatus;
310 return NS_OK;
313 NS_IMETHODIMP
314 nsBaseChannel::Cancel(nsresult status)
316 // Ignore redundant cancelation
317 if (NS_FAILED(mStatus))
318 return NS_OK;
320 mStatus = status;
322 if (mPump)
323 mPump->Cancel(status);
325 return NS_OK;
328 NS_IMETHODIMP
329 nsBaseChannel::Suspend()
331 NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
332 return mPump->Suspend();
335 NS_IMETHODIMP
336 nsBaseChannel::Resume()
338 NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
339 return mPump->Resume();
342 NS_IMETHODIMP
343 nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
345 *aLoadFlags = mLoadFlags;
346 return NS_OK;
349 NS_IMETHODIMP
350 nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
352 mLoadFlags = aLoadFlags;
353 return NS_OK;
356 NS_IMETHODIMP
357 nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
359 NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
360 return NS_OK;
363 NS_IMETHODIMP
364 nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
366 mLoadGroup = aLoadGroup;
367 CallbacksChanged();
368 return NS_OK;
371 //-----------------------------------------------------------------------------
372 // nsBaseChannel::nsIChannel
374 NS_IMETHODIMP
375 nsBaseChannel::GetOriginalURI(nsIURI **aURI)
377 *aURI = OriginalURI();
378 NS_ADDREF(*aURI);
379 return NS_OK;
382 NS_IMETHODIMP
383 nsBaseChannel::SetOriginalURI(nsIURI *aURI)
385 NS_ENSURE_ARG_POINTER(aURI);
386 mOriginalURI = aURI;
387 return NS_OK;
390 NS_IMETHODIMP
391 nsBaseChannel::GetURI(nsIURI **aURI)
393 NS_IF_ADDREF(*aURI = mURI);
394 return NS_OK;
397 NS_IMETHODIMP
398 nsBaseChannel::GetOwner(nsISupports **aOwner)
400 NS_IF_ADDREF(*aOwner = mOwner);
401 return NS_OK;
404 NS_IMETHODIMP
405 nsBaseChannel::SetOwner(nsISupports *aOwner)
407 mOwner = aOwner;
408 return NS_OK;
411 NS_IMETHODIMP
412 nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
414 NS_IF_ADDREF(*aCallbacks = mCallbacks);
415 return NS_OK;
418 NS_IMETHODIMP
419 nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
421 mCallbacks = aCallbacks;
422 CallbacksChanged();
423 return NS_OK;
426 NS_IMETHODIMP
427 nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
429 NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
430 return NS_OK;
433 NS_IMETHODIMP
434 nsBaseChannel::GetContentType(nsACString &aContentType)
436 aContentType = mContentType;
437 return NS_OK;
440 NS_IMETHODIMP
441 nsBaseChannel::SetContentType(const nsACString &aContentType)
443 // mContentCharset is unchanged if not parsed
444 PRBool dummy;
445 net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
446 return NS_OK;
449 NS_IMETHODIMP
450 nsBaseChannel::GetContentCharset(nsACString &aContentCharset)
452 aContentCharset = mContentCharset;
453 return NS_OK;
456 NS_IMETHODIMP
457 nsBaseChannel::SetContentCharset(const nsACString &aContentCharset)
459 mContentCharset = aContentCharset;
460 return NS_OK;
463 NS_IMETHODIMP
464 nsBaseChannel::GetContentLength(PRInt32 *aContentLength)
466 PRInt64 len = ContentLength64();
467 if (len > PR_INT32_MAX || len < 0)
468 *aContentLength = -1;
469 else
470 *aContentLength = (PRInt32) len;
471 return NS_OK;
474 NS_IMETHODIMP
475 nsBaseChannel::SetContentLength(PRInt32 aContentLength)
477 SetContentLength64(aContentLength);
478 return NS_OK;
481 NS_IMETHODIMP
482 nsBaseChannel::Open(nsIInputStream **result)
484 NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
485 NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
486 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
488 nsCOMPtr<nsIChannel> chan;
489 nsresult rv = OpenContentStream(PR_FALSE, result, getter_AddRefs(chan));
490 NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?");
491 if (NS_SUCCEEDED(rv) && chan) {
492 rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, PR_FALSE);
493 if (NS_FAILED(rv))
494 return rv;
495 rv = chan->Open(result);
496 } else if (rv == NS_ERROR_NOT_IMPLEMENTED)
497 return NS_ImplementChannelOpen(this, result);
499 mWasOpened = NS_SUCCEEDED(rv);
501 return rv;
504 NS_IMETHODIMP
505 nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
507 NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
508 NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
509 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
510 NS_ENSURE_ARG(listener);
512 // Ensure that this is an allowed port before proceeding.
513 nsresult rv = NS_CheckPortSafety(mURI);
514 if (NS_FAILED(rv)) {
515 mCallbacks = nsnull;
516 return rv;
519 // Store the listener and context early so that OpenContentStream and the
520 // stream's AsyncWait method (called by AsyncRead) can have access to them
521 // via PushStreamConverter and the StreamListener methods. However, since
522 // this typically introduces a reference cycle between this and the listener,
523 // we need to be sure to break the reference if this method does not succeed.
524 mListener = listener;
525 mListenerContext = ctxt;
527 // This method assigns mPump as a side-effect. We need to clear mPump if
528 // this method fails.
529 rv = BeginPumpingData();
530 if (NS_FAILED(rv)) {
531 mPump = nsnull;
532 mListener = nsnull;
533 mListenerContext = nsnull;
534 mCallbacks = nsnull;
535 return rv;
538 // At this point, we are going to return success no matter what.
540 mWasOpened = PR_TRUE;
542 SUSPEND_PUMP_FOR_SCOPE();
544 if (mLoadGroup)
545 mLoadGroup->AddRequest(this, nsnull);
547 return NS_OK;
550 //-----------------------------------------------------------------------------
551 // nsBaseChannel::nsITransportEventSink
553 NS_IMETHODIMP
554 nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status,
555 PRUint64 progress, PRUint64 progressMax)
557 // In some cases, we may wish to suppress transport-layer status events.
559 if (!mPump || NS_FAILED(mStatus) || HasLoadFlag(LOAD_BACKGROUND))
560 return NS_OK;
562 SUSPEND_PUMP_FOR_SCOPE();
564 // Lazily fetch mProgressSink
565 if (!mProgressSink) {
566 if (mQueriedProgressSink)
567 return NS_OK;
568 GetCallback(mProgressSink);
569 mQueriedProgressSink = PR_TRUE;
570 if (!mProgressSink)
571 return NS_OK;
574 nsAutoString statusArg;
575 if (GetStatusArg(status, statusArg))
576 mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get());
578 if (progress)
579 mProgressSink->OnProgress(this, mListenerContext, progress, progressMax);
581 return NS_OK;
584 //-----------------------------------------------------------------------------
585 // nsBaseChannel::nsIInterfaceRequestor
587 NS_IMETHODIMP
588 nsBaseChannel::GetInterface(const nsIID &iid, void **result)
590 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result);
591 return *result ? NS_OK : NS_ERROR_NO_INTERFACE;
594 //-----------------------------------------------------------------------------
595 // nsBaseChannel::nsIRequestObserver
597 static void
598 CallTypeSniffers(void *aClosure, const PRUint8 *aData, PRUint32 aCount)
600 nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
602 const nsCOMArray<nsIContentSniffer>& sniffers =
603 gIOService->GetContentSniffers();
604 PRUint32 length = sniffers.Count();
605 for (PRUint32 i = 0; i < length; ++i) {
606 nsCAutoString newType;
607 nsresult rv =
608 sniffers[i]->GetMIMETypeFromContent(chan, aData, aCount, newType);
609 if (NS_SUCCEEDED(rv) && !newType.IsEmpty()) {
610 chan->SetContentType(newType);
611 break;
616 static void
617 CallUnknownTypeSniffer(void *aClosure, const PRUint8 *aData, PRUint32 aCount)
619 nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
621 nsCOMPtr<nsIContentSniffer> sniffer =
622 do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER);
623 if (!sniffer)
624 return;
626 nsCAutoString detected;
627 nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected);
628 if (NS_SUCCEEDED(rv))
629 chan->SetContentType(detected);
632 NS_IMETHODIMP
633 nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
635 // If our content type is unknown, then use the content type sniffer. If the
636 // sniffer is not available for some reason, then we just keep going as-is.
637 if (NS_SUCCEEDED(mStatus) && mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
638 mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this));
641 // Now, the general type sniffers. Skip this if we have none.
642 if ((mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) &&
643 gIOService->GetContentSniffers().Count() != 0)
644 mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
646 SUSPEND_PUMP_FOR_SCOPE();
648 return mListener->OnStartRequest(this, mListenerContext);
651 NS_IMETHODIMP
652 nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
653 nsresult status)
655 // If both mStatus and status are failure codes, we keep mStatus as-is since
656 // that is consistent with our GetStatus and Cancel methods.
657 if (NS_SUCCEEDED(mStatus))
658 mStatus = status;
660 // Cause IsPending to return false.
661 mPump = nsnull;
663 mListener->OnStopRequest(this, mListenerContext, mStatus);
664 mListener = nsnull;
665 mListenerContext = nsnull;
667 // No need to suspend pump in this scope since we will not be receiving
668 // any more events from it.
670 if (mLoadGroup)
671 mLoadGroup->RemoveRequest(this, nsnull, mStatus);
673 // Drop notification callbacks to prevent cycles.
674 mCallbacks = nsnull;
675 CallbacksChanged();
677 return NS_OK;
680 //-----------------------------------------------------------------------------
681 // nsBaseChannel::nsIStreamListener
683 NS_IMETHODIMP
684 nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
685 nsIInputStream *stream, PRUint32 offset,
686 PRUint32 count)
688 SUSPEND_PUMP_FOR_SCOPE();
690 nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream,
691 offset, count);
692 if (mSynthProgressEvents && NS_SUCCEEDED(rv)) {
693 PRUint64 prog = PRUint64(offset) + count;
694 PRUint64 progMax = ContentLength64();
695 OnTransportStatus(nsnull, nsITransport::STATUS_READING, prog, progMax);
698 return rv;