1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsBaseChannel.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,784 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsBaseChannel.h" 1.10 +#include "nsURLHelper.h" 1.11 +#include "nsNetUtil.h" 1.12 +#include "nsMimeTypes.h" 1.13 +#include "nsIHttpEventSink.h" 1.14 +#include "nsIHttpChannel.h" 1.15 +#include "nsIChannelEventSink.h" 1.16 +#include "nsIStreamConverterService.h" 1.17 +#include "nsChannelClassifier.h" 1.18 +#include "nsAsyncRedirectVerifyHelper.h" 1.19 + 1.20 +static PLDHashOperator 1.21 +CopyProperties(const nsAString &key, nsIVariant *data, void *closure) 1.22 +{ 1.23 + nsIWritablePropertyBag *bag = 1.24 + static_cast<nsIWritablePropertyBag *>(closure); 1.25 + 1.26 + bag->SetProperty(key, data); 1.27 + return PL_DHASH_NEXT; 1.28 +} 1.29 + 1.30 +// This class is used to suspend a request across a function scope. 1.31 +class ScopedRequestSuspender { 1.32 +public: 1.33 + ScopedRequestSuspender(nsIRequest *request) 1.34 + : mRequest(request) { 1.35 + if (mRequest && NS_FAILED(mRequest->Suspend())) { 1.36 + NS_WARNING("Couldn't suspend pump"); 1.37 + mRequest = nullptr; 1.38 + } 1.39 + } 1.40 + ~ScopedRequestSuspender() { 1.41 + if (mRequest) 1.42 + mRequest->Resume(); 1.43 + } 1.44 +private: 1.45 + nsIRequest *mRequest; 1.46 +}; 1.47 + 1.48 +// Used to suspend data events from mPump within a function scope. This is 1.49 +// usually needed when a function makes callbacks that could process events. 1.50 +#define SUSPEND_PUMP_FOR_SCOPE() \ 1.51 + ScopedRequestSuspender pump_suspender__(mPump) 1.52 + 1.53 +//----------------------------------------------------------------------------- 1.54 +// nsBaseChannel 1.55 + 1.56 +nsBaseChannel::nsBaseChannel() 1.57 + : mLoadFlags(LOAD_NORMAL) 1.58 + , mQueriedProgressSink(true) 1.59 + , mSynthProgressEvents(false) 1.60 + , mWasOpened(false) 1.61 + , mWaitingOnAsyncRedirect(false) 1.62 + , mStatus(NS_OK) 1.63 + , mContentDispositionHint(UINT32_MAX) 1.64 + , mContentLength(-1) 1.65 +{ 1.66 + mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); 1.67 +} 1.68 + 1.69 +nsresult 1.70 +nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags, 1.71 + bool openNewChannel) 1.72 +{ 1.73 + SUSPEND_PUMP_FOR_SCOPE(); 1.74 + 1.75 + // Transfer properties 1.76 + 1.77 + newChannel->SetLoadGroup(mLoadGroup); 1.78 + newChannel->SetNotificationCallbacks(mCallbacks); 1.79 + newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE); 1.80 + 1.81 + // Try to preserve the privacy bit if it has been overridden 1.82 + if (mPrivateBrowsingOverriden) { 1.83 + nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel = 1.84 + do_QueryInterface(newChannel); 1.85 + if (newPBChannel) { 1.86 + newPBChannel->SetPrivate(mPrivateBrowsing); 1.87 + } 1.88 + } 1.89 + 1.90 + nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel); 1.91 + if (bag) 1.92 + mPropertyHash.EnumerateRead(CopyProperties, bag.get()); 1.93 + 1.94 + // Notify consumer, giving chance to cancel redirect. For backwards compat, 1.95 + // we support nsIHttpEventSink if we are an HTTP channel and if this is not 1.96 + // an internal redirect. 1.97 + 1.98 + nsRefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper = 1.99 + new nsAsyncRedirectVerifyHelper(); 1.100 + 1.101 + bool checkRedirectSynchronously = !openNewChannel; 1.102 + 1.103 + mRedirectChannel = newChannel; 1.104 + mRedirectFlags = redirectFlags; 1.105 + mOpenRedirectChannel = openNewChannel; 1.106 + nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags, 1.107 + checkRedirectSynchronously); 1.108 + if (NS_FAILED(rv)) 1.109 + return rv; 1.110 + 1.111 + if (checkRedirectSynchronously && NS_FAILED(mStatus)) 1.112 + return mStatus; 1.113 + 1.114 + return NS_OK; 1.115 +} 1.116 + 1.117 +nsresult 1.118 +nsBaseChannel::ContinueRedirect() 1.119 +{ 1.120 + // Backwards compat for non-internal redirects from a HTTP channel. 1.121 + // XXX Is our http channel implementation going to derive from nsBaseChannel? 1.122 + // If not, this code can be removed. 1.123 + if (!(mRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { 1.124 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(); 1.125 + if (httpChannel) { 1.126 + nsCOMPtr<nsIHttpEventSink> httpEventSink; 1.127 + GetCallback(httpEventSink); 1.128 + if (httpEventSink) { 1.129 + nsresult rv = httpEventSink->OnRedirect(httpChannel, mRedirectChannel); 1.130 + if (NS_FAILED(rv)) { 1.131 + return rv; 1.132 + } 1.133 + } 1.134 + } 1.135 + } 1.136 + 1.137 + // Make sure to do this _after_ making all the OnChannelRedirect calls 1.138 + mRedirectChannel->SetOriginalURI(OriginalURI()); 1.139 + 1.140 + // If we fail to open the new channel, then we want to leave this channel 1.141 + // unaffected, so we defer tearing down our channel until we have succeeded 1.142 + // with the redirect. 1.143 + 1.144 + if (mOpenRedirectChannel) { 1.145 + nsresult rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); 1.146 + if (NS_FAILED(rv)) 1.147 + return rv; 1.148 + } 1.149 + 1.150 + mRedirectChannel = nullptr; 1.151 + 1.152 + // close down this channel 1.153 + Cancel(NS_BINDING_REDIRECTED); 1.154 + ChannelDone(); 1.155 + 1.156 + return NS_OK; 1.157 +} 1.158 + 1.159 +bool 1.160 +nsBaseChannel::HasContentTypeHint() const 1.161 +{ 1.162 + NS_ASSERTION(!Pending(), "HasContentTypeHint called too late"); 1.163 + return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE); 1.164 +} 1.165 + 1.166 +nsresult 1.167 +nsBaseChannel::PushStreamConverter(const char *fromType, 1.168 + const char *toType, 1.169 + bool invalidatesContentLength, 1.170 + nsIStreamListener **result) 1.171 +{ 1.172 + NS_ASSERTION(mListener, "no listener"); 1.173 + 1.174 + nsresult rv; 1.175 + nsCOMPtr<nsIStreamConverterService> scs = 1.176 + do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); 1.177 + if (NS_FAILED(rv)) 1.178 + return rv; 1.179 + 1.180 + nsCOMPtr<nsIStreamListener> converter; 1.181 + rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext, 1.182 + getter_AddRefs(converter)); 1.183 + if (NS_SUCCEEDED(rv)) { 1.184 + mListener = converter; 1.185 + if (invalidatesContentLength) 1.186 + mContentLength = -1; 1.187 + if (result) { 1.188 + *result = nullptr; 1.189 + converter.swap(*result); 1.190 + } 1.191 + } 1.192 + return rv; 1.193 +} 1.194 + 1.195 +nsresult 1.196 +nsBaseChannel::BeginPumpingData() 1.197 +{ 1.198 + nsCOMPtr<nsIInputStream> stream; 1.199 + nsCOMPtr<nsIChannel> channel; 1.200 + nsresult rv = OpenContentStream(true, getter_AddRefs(stream), 1.201 + getter_AddRefs(channel)); 1.202 + if (NS_FAILED(rv)) 1.203 + return rv; 1.204 + 1.205 + NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?"); 1.206 + 1.207 + if (channel) { 1.208 + rv = NS_DispatchToCurrentThread(new RedirectRunnable(this, channel)); 1.209 + if (NS_SUCCEEDED(rv)) 1.210 + mWaitingOnAsyncRedirect = true; 1.211 + return rv; 1.212 + } 1.213 + 1.214 + // By assigning mPump, we flag this channel as pending (see Pending). It's 1.215 + // important that the pending flag is set when we call into the stream (the 1.216 + // call to AsyncRead results in the stream's AsyncWait method being called) 1.217 + // and especially when we call into the loadgroup. Our caller takes care to 1.218 + // release mPump if we return an error. 1.219 + 1.220 + rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0, 1.221 + true); 1.222 + if (NS_SUCCEEDED(rv)) 1.223 + rv = mPump->AsyncRead(this, nullptr); 1.224 + 1.225 + return rv; 1.226 +} 1.227 + 1.228 +void 1.229 +nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel) 1.230 +{ 1.231 + NS_ASSERTION(!mPump, "Shouldn't have gotten here"); 1.232 + 1.233 + nsresult rv = mStatus; 1.234 + if (NS_SUCCEEDED(mStatus)) { 1.235 + rv = Redirect(newChannel, 1.236 + nsIChannelEventSink::REDIRECT_TEMPORARY, 1.237 + true); 1.238 + if (NS_SUCCEEDED(rv)) { 1.239 + // OnRedirectVerifyCallback will be called asynchronously 1.240 + return; 1.241 + } 1.242 + } 1.243 + 1.244 + ContinueHandleAsyncRedirect(rv); 1.245 +} 1.246 + 1.247 +void 1.248 +nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result) 1.249 +{ 1.250 + mWaitingOnAsyncRedirect = false; 1.251 + 1.252 + if (NS_FAILED(result)) 1.253 + Cancel(result); 1.254 + 1.255 + if (NS_FAILED(result) && mListener) { 1.256 + // Notify our consumer ourselves 1.257 + mListener->OnStartRequest(this, mListenerContext); 1.258 + mListener->OnStopRequest(this, mListenerContext, mStatus); 1.259 + ChannelDone(); 1.260 + } 1.261 + 1.262 + if (mLoadGroup) 1.263 + mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1.264 + 1.265 + // Drop notification callbacks to prevent cycles. 1.266 + mCallbacks = nullptr; 1.267 + CallbacksChanged(); 1.268 +} 1.269 + 1.270 +void 1.271 +nsBaseChannel::ClassifyURI() 1.272 +{ 1.273 + nsresult rv; 1.274 + 1.275 + if (mLoadFlags & LOAD_CLASSIFY_URI) { 1.276 + nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier(); 1.277 + if (classifier) { 1.278 + rv = classifier->Start(this); 1.279 + if (NS_FAILED(rv)) { 1.280 + Cancel(rv); 1.281 + } 1.282 + } else { 1.283 + Cancel(NS_ERROR_OUT_OF_MEMORY); 1.284 + } 1.285 + } 1.286 +} 1.287 + 1.288 +//----------------------------------------------------------------------------- 1.289 +// nsBaseChannel::nsISupports 1.290 + 1.291 +NS_IMPL_ISUPPORTS_INHERITED(nsBaseChannel, 1.292 + nsHashPropertyBag, 1.293 + nsIRequest, 1.294 + nsIChannel, 1.295 + nsIInterfaceRequestor, 1.296 + nsITransportEventSink, 1.297 + nsIRequestObserver, 1.298 + nsIStreamListener, 1.299 + nsIAsyncVerifyRedirectCallback, 1.300 + nsIPrivateBrowsingChannel) 1.301 + 1.302 +//----------------------------------------------------------------------------- 1.303 +// nsBaseChannel::nsIRequest 1.304 + 1.305 +NS_IMETHODIMP 1.306 +nsBaseChannel::GetName(nsACString &result) 1.307 +{ 1.308 + if (!mURI) { 1.309 + result.Truncate(); 1.310 + return NS_OK; 1.311 + } 1.312 + return mURI->GetSpec(result); 1.313 +} 1.314 + 1.315 +NS_IMETHODIMP 1.316 +nsBaseChannel::IsPending(bool *result) 1.317 +{ 1.318 + *result = Pending(); 1.319 + return NS_OK; 1.320 +} 1.321 + 1.322 +NS_IMETHODIMP 1.323 +nsBaseChannel::GetStatus(nsresult *status) 1.324 +{ 1.325 + if (mPump && NS_SUCCEEDED(mStatus)) { 1.326 + mPump->GetStatus(status); 1.327 + } else { 1.328 + *status = mStatus; 1.329 + } 1.330 + return NS_OK; 1.331 +} 1.332 + 1.333 +NS_IMETHODIMP 1.334 +nsBaseChannel::Cancel(nsresult status) 1.335 +{ 1.336 + // Ignore redundant cancelation 1.337 + if (NS_FAILED(mStatus)) 1.338 + return NS_OK; 1.339 + 1.340 + mStatus = status; 1.341 + 1.342 + if (mPump) 1.343 + mPump->Cancel(status); 1.344 + 1.345 + return NS_OK; 1.346 +} 1.347 + 1.348 +NS_IMETHODIMP 1.349 +nsBaseChannel::Suspend() 1.350 +{ 1.351 + NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED); 1.352 + return mPump->Suspend(); 1.353 +} 1.354 + 1.355 +NS_IMETHODIMP 1.356 +nsBaseChannel::Resume() 1.357 +{ 1.358 + NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED); 1.359 + return mPump->Resume(); 1.360 +} 1.361 + 1.362 +NS_IMETHODIMP 1.363 +nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) 1.364 +{ 1.365 + *aLoadFlags = mLoadFlags; 1.366 + return NS_OK; 1.367 +} 1.368 + 1.369 +NS_IMETHODIMP 1.370 +nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) 1.371 +{ 1.372 + mLoadFlags = aLoadFlags; 1.373 + return NS_OK; 1.374 +} 1.375 + 1.376 +NS_IMETHODIMP 1.377 +nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) 1.378 +{ 1.379 + NS_IF_ADDREF(*aLoadGroup = mLoadGroup); 1.380 + return NS_OK; 1.381 +} 1.382 + 1.383 +NS_IMETHODIMP 1.384 +nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) 1.385 +{ 1.386 + if (!CanSetLoadGroup(aLoadGroup)) { 1.387 + return NS_ERROR_FAILURE; 1.388 + } 1.389 + 1.390 + mLoadGroup = aLoadGroup; 1.391 + CallbacksChanged(); 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +//----------------------------------------------------------------------------- 1.396 +// nsBaseChannel::nsIChannel 1.397 + 1.398 +NS_IMETHODIMP 1.399 +nsBaseChannel::GetOriginalURI(nsIURI **aURI) 1.400 +{ 1.401 + *aURI = OriginalURI(); 1.402 + NS_ADDREF(*aURI); 1.403 + return NS_OK; 1.404 +} 1.405 + 1.406 +NS_IMETHODIMP 1.407 +nsBaseChannel::SetOriginalURI(nsIURI *aURI) 1.408 +{ 1.409 + NS_ENSURE_ARG_POINTER(aURI); 1.410 + mOriginalURI = aURI; 1.411 + return NS_OK; 1.412 +} 1.413 + 1.414 +NS_IMETHODIMP 1.415 +nsBaseChannel::GetURI(nsIURI **aURI) 1.416 +{ 1.417 + NS_IF_ADDREF(*aURI = mURI); 1.418 + return NS_OK; 1.419 +} 1.420 + 1.421 +NS_IMETHODIMP 1.422 +nsBaseChannel::GetOwner(nsISupports **aOwner) 1.423 +{ 1.424 + NS_IF_ADDREF(*aOwner = mOwner); 1.425 + return NS_OK; 1.426 +} 1.427 + 1.428 +NS_IMETHODIMP 1.429 +nsBaseChannel::SetOwner(nsISupports *aOwner) 1.430 +{ 1.431 + mOwner = aOwner; 1.432 + return NS_OK; 1.433 +} 1.434 + 1.435 +NS_IMETHODIMP 1.436 +nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) 1.437 +{ 1.438 + NS_IF_ADDREF(*aCallbacks = mCallbacks); 1.439 + return NS_OK; 1.440 +} 1.441 + 1.442 +NS_IMETHODIMP 1.443 +nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) 1.444 +{ 1.445 + if (!CanSetCallbacks(aCallbacks)) { 1.446 + return NS_ERROR_FAILURE; 1.447 + } 1.448 + 1.449 + mCallbacks = aCallbacks; 1.450 + CallbacksChanged(); 1.451 + return NS_OK; 1.452 +} 1.453 + 1.454 +NS_IMETHODIMP 1.455 +nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo) 1.456 +{ 1.457 + NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); 1.458 + return NS_OK; 1.459 +} 1.460 + 1.461 +NS_IMETHODIMP 1.462 +nsBaseChannel::GetContentType(nsACString &aContentType) 1.463 +{ 1.464 + aContentType = mContentType; 1.465 + return NS_OK; 1.466 +} 1.467 + 1.468 +NS_IMETHODIMP 1.469 +nsBaseChannel::SetContentType(const nsACString &aContentType) 1.470 +{ 1.471 + // mContentCharset is unchanged if not parsed 1.472 + bool dummy; 1.473 + net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy); 1.474 + return NS_OK; 1.475 +} 1.476 + 1.477 +NS_IMETHODIMP 1.478 +nsBaseChannel::GetContentCharset(nsACString &aContentCharset) 1.479 +{ 1.480 + aContentCharset = mContentCharset; 1.481 + return NS_OK; 1.482 +} 1.483 + 1.484 +NS_IMETHODIMP 1.485 +nsBaseChannel::SetContentCharset(const nsACString &aContentCharset) 1.486 +{ 1.487 + mContentCharset = aContentCharset; 1.488 + return NS_OK; 1.489 +} 1.490 + 1.491 +NS_IMETHODIMP 1.492 +nsBaseChannel::GetContentDisposition(uint32_t *aContentDisposition) 1.493 +{ 1.494 + // preserve old behavior, fail unless explicitly set. 1.495 + if (mContentDispositionHint == UINT32_MAX) { 1.496 + return NS_ERROR_NOT_AVAILABLE; 1.497 + } 1.498 + 1.499 + *aContentDisposition = mContentDispositionHint; 1.500 + return NS_OK; 1.501 +} 1.502 + 1.503 +NS_IMETHODIMP 1.504 +nsBaseChannel::SetContentDisposition(uint32_t aContentDisposition) 1.505 +{ 1.506 + mContentDispositionHint = aContentDisposition; 1.507 + return NS_OK; 1.508 +} 1.509 + 1.510 +NS_IMETHODIMP 1.511 +nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) 1.512 +{ 1.513 + if (!mContentDispositionFilename) { 1.514 + return NS_ERROR_NOT_AVAILABLE; 1.515 + } 1.516 + 1.517 + aContentDispositionFilename = *mContentDispositionFilename; 1.518 + return NS_OK; 1.519 +} 1.520 + 1.521 +NS_IMETHODIMP 1.522 +nsBaseChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) 1.523 +{ 1.524 + mContentDispositionFilename = new nsString(aContentDispositionFilename); 1.525 + return NS_OK; 1.526 +} 1.527 + 1.528 +NS_IMETHODIMP 1.529 +nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) 1.530 +{ 1.531 + return NS_ERROR_NOT_AVAILABLE; 1.532 +} 1.533 + 1.534 +NS_IMETHODIMP 1.535 +nsBaseChannel::GetContentLength(int64_t *aContentLength) 1.536 +{ 1.537 + *aContentLength = mContentLength; 1.538 + return NS_OK; 1.539 +} 1.540 + 1.541 +NS_IMETHODIMP 1.542 +nsBaseChannel::SetContentLength(int64_t aContentLength) 1.543 +{ 1.544 + mContentLength = aContentLength; 1.545 + return NS_OK; 1.546 +} 1.547 + 1.548 +NS_IMETHODIMP 1.549 +nsBaseChannel::Open(nsIInputStream **result) 1.550 +{ 1.551 + NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); 1.552 + NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); 1.553 + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); 1.554 + 1.555 + nsCOMPtr<nsIChannel> chan; 1.556 + nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan)); 1.557 + NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?"); 1.558 + if (NS_SUCCEEDED(rv) && chan) { 1.559 + rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false); 1.560 + if (NS_FAILED(rv)) 1.561 + return rv; 1.562 + rv = chan->Open(result); 1.563 + } else if (rv == NS_ERROR_NOT_IMPLEMENTED) 1.564 + return NS_ImplementChannelOpen(this, result); 1.565 + 1.566 + if (NS_SUCCEEDED(rv)) { 1.567 + mWasOpened = true; 1.568 + ClassifyURI(); 1.569 + } 1.570 + 1.571 + return rv; 1.572 +} 1.573 + 1.574 +NS_IMETHODIMP 1.575 +nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) 1.576 +{ 1.577 + NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); 1.578 + NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); 1.579 + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); 1.580 + NS_ENSURE_ARG(listener); 1.581 + 1.582 + // Ensure that this is an allowed port before proceeding. 1.583 + nsresult rv = NS_CheckPortSafety(mURI); 1.584 + if (NS_FAILED(rv)) { 1.585 + mCallbacks = nullptr; 1.586 + return rv; 1.587 + } 1.588 + 1.589 + // Store the listener and context early so that OpenContentStream and the 1.590 + // stream's AsyncWait method (called by AsyncRead) can have access to them 1.591 + // via PushStreamConverter and the StreamListener methods. However, since 1.592 + // this typically introduces a reference cycle between this and the listener, 1.593 + // we need to be sure to break the reference if this method does not succeed. 1.594 + mListener = listener; 1.595 + mListenerContext = ctxt; 1.596 + 1.597 + // This method assigns mPump as a side-effect. We need to clear mPump if 1.598 + // this method fails. 1.599 + rv = BeginPumpingData(); 1.600 + if (NS_FAILED(rv)) { 1.601 + mPump = nullptr; 1.602 + ChannelDone(); 1.603 + mCallbacks = nullptr; 1.604 + return rv; 1.605 + } 1.606 + 1.607 + // At this point, we are going to return success no matter what. 1.608 + 1.609 + mWasOpened = true; 1.610 + 1.611 + SUSPEND_PUMP_FOR_SCOPE(); 1.612 + 1.613 + if (mLoadGroup) 1.614 + mLoadGroup->AddRequest(this, nullptr); 1.615 + 1.616 + ClassifyURI(); 1.617 + 1.618 + return NS_OK; 1.619 +} 1.620 + 1.621 +//----------------------------------------------------------------------------- 1.622 +// nsBaseChannel::nsITransportEventSink 1.623 + 1.624 +NS_IMETHODIMP 1.625 +nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status, 1.626 + uint64_t progress, uint64_t progressMax) 1.627 +{ 1.628 + // In some cases, we may wish to suppress transport-layer status events. 1.629 + 1.630 + if (!mPump || NS_FAILED(mStatus) || HasLoadFlag(LOAD_BACKGROUND)) 1.631 + return NS_OK; 1.632 + 1.633 + SUSPEND_PUMP_FOR_SCOPE(); 1.634 + 1.635 + // Lazily fetch mProgressSink 1.636 + if (!mProgressSink) { 1.637 + if (mQueriedProgressSink) 1.638 + return NS_OK; 1.639 + GetCallback(mProgressSink); 1.640 + mQueriedProgressSink = true; 1.641 + if (!mProgressSink) 1.642 + return NS_OK; 1.643 + } 1.644 + 1.645 + nsAutoString statusArg; 1.646 + if (GetStatusArg(status, statusArg)) 1.647 + mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get()); 1.648 + 1.649 + if (progress) 1.650 + mProgressSink->OnProgress(this, mListenerContext, progress, progressMax); 1.651 + 1.652 + return NS_OK; 1.653 +} 1.654 + 1.655 +//----------------------------------------------------------------------------- 1.656 +// nsBaseChannel::nsIInterfaceRequestor 1.657 + 1.658 +NS_IMETHODIMP 1.659 +nsBaseChannel::GetInterface(const nsIID &iid, void **result) 1.660 +{ 1.661 + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result); 1.662 + return *result ? NS_OK : NS_ERROR_NO_INTERFACE; 1.663 +} 1.664 + 1.665 +//----------------------------------------------------------------------------- 1.666 +// nsBaseChannel::nsIRequestObserver 1.667 + 1.668 +static void 1.669 +CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount) 1.670 +{ 1.671 + nsIChannel *chan = static_cast<nsIChannel*>(aClosure); 1.672 + 1.673 + nsAutoCString newType; 1.674 + NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType); 1.675 + if (!newType.IsEmpty()) { 1.676 + chan->SetContentType(newType); 1.677 + } 1.678 +} 1.679 + 1.680 +static void 1.681 +CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount) 1.682 +{ 1.683 + nsIChannel *chan = static_cast<nsIChannel*>(aClosure); 1.684 + 1.685 + nsCOMPtr<nsIContentSniffer> sniffer = 1.686 + do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER); 1.687 + if (!sniffer) 1.688 + return; 1.689 + 1.690 + nsAutoCString detected; 1.691 + nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected); 1.692 + if (NS_SUCCEEDED(rv)) 1.693 + chan->SetContentType(detected); 1.694 +} 1.695 + 1.696 +NS_IMETHODIMP 1.697 +nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) 1.698 +{ 1.699 + // If our content type is unknown or if the content type is 1.700 + // application/octet-stream and the caller requested it, use the content type 1.701 + // sniffer. If the sniffer is not available for some reason, then we just keep 1.702 + // going as-is. 1.703 + bool shouldSniff = mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) || 1.704 + ((mLoadFlags & LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN) && 1.705 + mContentType.EqualsLiteral(APPLICATION_OCTET_STREAM)); 1.706 + 1.707 + if (NS_SUCCEEDED(mStatus) && shouldSniff) { 1.708 + mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this)); 1.709 + } 1.710 + 1.711 + // Now, the general type sniffers. Skip this if we have none. 1.712 + if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) 1.713 + mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this)); 1.714 + 1.715 + SUSPEND_PUMP_FOR_SCOPE(); 1.716 + 1.717 + if (mListener) // null in case of redirect 1.718 + return mListener->OnStartRequest(this, mListenerContext); 1.719 + return NS_OK; 1.720 +} 1.721 + 1.722 +NS_IMETHODIMP 1.723 +nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, 1.724 + nsresult status) 1.725 +{ 1.726 + // If both mStatus and status are failure codes, we keep mStatus as-is since 1.727 + // that is consistent with our GetStatus and Cancel methods. 1.728 + if (NS_SUCCEEDED(mStatus)) 1.729 + mStatus = status; 1.730 + 1.731 + // Cause Pending to return false. 1.732 + mPump = nullptr; 1.733 + 1.734 + if (mListener) // null in case of redirect 1.735 + mListener->OnStopRequest(this, mListenerContext, mStatus); 1.736 + ChannelDone(); 1.737 + 1.738 + // No need to suspend pump in this scope since we will not be receiving 1.739 + // any more events from it. 1.740 + 1.741 + if (mLoadGroup) 1.742 + mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1.743 + 1.744 + // Drop notification callbacks to prevent cycles. 1.745 + mCallbacks = nullptr; 1.746 + CallbacksChanged(); 1.747 + 1.748 + return NS_OK; 1.749 +} 1.750 + 1.751 +//----------------------------------------------------------------------------- 1.752 +// nsBaseChannel::nsIStreamListener 1.753 + 1.754 +NS_IMETHODIMP 1.755 +nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, 1.756 + nsIInputStream *stream, uint64_t offset, 1.757 + uint32_t count) 1.758 +{ 1.759 + SUSPEND_PUMP_FOR_SCOPE(); 1.760 + 1.761 + nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream, 1.762 + offset, count); 1.763 + if (mSynthProgressEvents && NS_SUCCEEDED(rv)) { 1.764 + uint64_t prog = offset + count; 1.765 + OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength); 1.766 + } 1.767 + 1.768 + return rv; 1.769 +} 1.770 + 1.771 +NS_IMETHODIMP 1.772 +nsBaseChannel::OnRedirectVerifyCallback(nsresult result) 1.773 +{ 1.774 + if (NS_SUCCEEDED(result)) 1.775 + result = ContinueRedirect(); 1.776 + 1.777 + if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) { 1.778 + if (NS_SUCCEEDED(mStatus)) 1.779 + mStatus = result; 1.780 + return NS_OK; 1.781 + } 1.782 + 1.783 + if (mWaitingOnAsyncRedirect) 1.784 + ContinueHandleAsyncRedirect(result); 1.785 + 1.786 + return NS_OK; 1.787 +}