michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsBaseChannel.h" michael@0: #include "nsURLHelper.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsMimeTypes.h" michael@0: #include "nsIHttpEventSink.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsIChannelEventSink.h" michael@0: #include "nsIStreamConverterService.h" michael@0: #include "nsChannelClassifier.h" michael@0: #include "nsAsyncRedirectVerifyHelper.h" michael@0: michael@0: static PLDHashOperator michael@0: CopyProperties(const nsAString &key, nsIVariant *data, void *closure) michael@0: { michael@0: nsIWritablePropertyBag *bag = michael@0: static_cast(closure); michael@0: michael@0: bag->SetProperty(key, data); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // This class is used to suspend a request across a function scope. michael@0: class ScopedRequestSuspender { michael@0: public: michael@0: ScopedRequestSuspender(nsIRequest *request) michael@0: : mRequest(request) { michael@0: if (mRequest && NS_FAILED(mRequest->Suspend())) { michael@0: NS_WARNING("Couldn't suspend pump"); michael@0: mRequest = nullptr; michael@0: } michael@0: } michael@0: ~ScopedRequestSuspender() { michael@0: if (mRequest) michael@0: mRequest->Resume(); michael@0: } michael@0: private: michael@0: nsIRequest *mRequest; michael@0: }; michael@0: michael@0: // Used to suspend data events from mPump within a function scope. This is michael@0: // usually needed when a function makes callbacks that could process events. michael@0: #define SUSPEND_PUMP_FOR_SCOPE() \ michael@0: ScopedRequestSuspender pump_suspender__(mPump) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel michael@0: michael@0: nsBaseChannel::nsBaseChannel() michael@0: : mLoadFlags(LOAD_NORMAL) michael@0: , mQueriedProgressSink(true) michael@0: , mSynthProgressEvents(false) michael@0: , mWasOpened(false) michael@0: , mWaitingOnAsyncRedirect(false) michael@0: , mStatus(NS_OK) michael@0: , mContentDispositionHint(UINT32_MAX) michael@0: , mContentLength(-1) michael@0: { michael@0: mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); michael@0: } michael@0: michael@0: nsresult michael@0: nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags, michael@0: bool openNewChannel) michael@0: { michael@0: SUSPEND_PUMP_FOR_SCOPE(); michael@0: michael@0: // Transfer properties michael@0: michael@0: newChannel->SetLoadGroup(mLoadGroup); michael@0: newChannel->SetNotificationCallbacks(mCallbacks); michael@0: newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE); michael@0: michael@0: // Try to preserve the privacy bit if it has been overridden michael@0: if (mPrivateBrowsingOverriden) { michael@0: nsCOMPtr newPBChannel = michael@0: do_QueryInterface(newChannel); michael@0: if (newPBChannel) { michael@0: newPBChannel->SetPrivate(mPrivateBrowsing); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr bag = ::do_QueryInterface(newChannel); michael@0: if (bag) michael@0: mPropertyHash.EnumerateRead(CopyProperties, bag.get()); michael@0: michael@0: // Notify consumer, giving chance to cancel redirect. For backwards compat, michael@0: // we support nsIHttpEventSink if we are an HTTP channel and if this is not michael@0: // an internal redirect. michael@0: michael@0: nsRefPtr redirectCallbackHelper = michael@0: new nsAsyncRedirectVerifyHelper(); michael@0: michael@0: bool checkRedirectSynchronously = !openNewChannel; michael@0: michael@0: mRedirectChannel = newChannel; michael@0: mRedirectFlags = redirectFlags; michael@0: mOpenRedirectChannel = openNewChannel; michael@0: nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags, michael@0: checkRedirectSynchronously); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (checkRedirectSynchronously && NS_FAILED(mStatus)) michael@0: return mStatus; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsBaseChannel::ContinueRedirect() michael@0: { michael@0: // Backwards compat for non-internal redirects from a HTTP channel. michael@0: // XXX Is our http channel implementation going to derive from nsBaseChannel? michael@0: // If not, this code can be removed. michael@0: if (!(mRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { michael@0: nsCOMPtr httpChannel = do_QueryInterface(); michael@0: if (httpChannel) { michael@0: nsCOMPtr httpEventSink; michael@0: GetCallback(httpEventSink); michael@0: if (httpEventSink) { michael@0: nsresult rv = httpEventSink->OnRedirect(httpChannel, mRedirectChannel); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Make sure to do this _after_ making all the OnChannelRedirect calls michael@0: mRedirectChannel->SetOriginalURI(OriginalURI()); michael@0: michael@0: // If we fail to open the new channel, then we want to leave this channel michael@0: // unaffected, so we defer tearing down our channel until we have succeeded michael@0: // with the redirect. michael@0: michael@0: if (mOpenRedirectChannel) { michael@0: nsresult rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: mRedirectChannel = nullptr; michael@0: michael@0: // close down this channel michael@0: Cancel(NS_BINDING_REDIRECTED); michael@0: ChannelDone(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsBaseChannel::HasContentTypeHint() const michael@0: { michael@0: NS_ASSERTION(!Pending(), "HasContentTypeHint called too late"); michael@0: return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE); michael@0: } michael@0: michael@0: nsresult michael@0: nsBaseChannel::PushStreamConverter(const char *fromType, michael@0: const char *toType, michael@0: bool invalidatesContentLength, michael@0: nsIStreamListener **result) michael@0: { michael@0: NS_ASSERTION(mListener, "no listener"); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr scs = michael@0: do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr converter; michael@0: rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext, michael@0: getter_AddRefs(converter)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mListener = converter; michael@0: if (invalidatesContentLength) michael@0: mContentLength = -1; michael@0: if (result) { michael@0: *result = nullptr; michael@0: converter.swap(*result); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsBaseChannel::BeginPumpingData() michael@0: { michael@0: nsCOMPtr stream; michael@0: nsCOMPtr channel; michael@0: nsresult rv = OpenContentStream(true, getter_AddRefs(stream), michael@0: getter_AddRefs(channel)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?"); michael@0: michael@0: if (channel) { michael@0: rv = NS_DispatchToCurrentThread(new RedirectRunnable(this, channel)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mWaitingOnAsyncRedirect = true; michael@0: return rv; michael@0: } michael@0: michael@0: // By assigning mPump, we flag this channel as pending (see Pending). It's michael@0: // important that the pending flag is set when we call into the stream (the michael@0: // call to AsyncRead results in the stream's AsyncWait method being called) michael@0: // and especially when we call into the loadgroup. Our caller takes care to michael@0: // release mPump if we return an error. michael@0: michael@0: rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0, michael@0: true); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = mPump->AsyncRead(this, nullptr); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel) michael@0: { michael@0: NS_ASSERTION(!mPump, "Shouldn't have gotten here"); michael@0: michael@0: nsresult rv = mStatus; michael@0: if (NS_SUCCEEDED(mStatus)) { michael@0: rv = Redirect(newChannel, michael@0: nsIChannelEventSink::REDIRECT_TEMPORARY, michael@0: true); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // OnRedirectVerifyCallback will be called asynchronously michael@0: return; michael@0: } michael@0: } michael@0: michael@0: ContinueHandleAsyncRedirect(rv); michael@0: } michael@0: michael@0: void michael@0: nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result) michael@0: { michael@0: mWaitingOnAsyncRedirect = false; michael@0: michael@0: if (NS_FAILED(result)) michael@0: Cancel(result); michael@0: michael@0: if (NS_FAILED(result) && mListener) { michael@0: // Notify our consumer ourselves michael@0: mListener->OnStartRequest(this, mListenerContext); michael@0: mListener->OnStopRequest(this, mListenerContext, mStatus); michael@0: ChannelDone(); michael@0: } michael@0: michael@0: if (mLoadGroup) michael@0: mLoadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: michael@0: // Drop notification callbacks to prevent cycles. michael@0: mCallbacks = nullptr; michael@0: CallbacksChanged(); michael@0: } michael@0: michael@0: void michael@0: nsBaseChannel::ClassifyURI() michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (mLoadFlags & LOAD_CLASSIFY_URI) { michael@0: nsRefPtr classifier = new nsChannelClassifier(); michael@0: if (classifier) { michael@0: rv = classifier->Start(this); michael@0: if (NS_FAILED(rv)) { michael@0: Cancel(rv); michael@0: } michael@0: } else { michael@0: Cancel(NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: } michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel::nsISupports michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsBaseChannel, michael@0: nsHashPropertyBag, michael@0: nsIRequest, michael@0: nsIChannel, michael@0: nsIInterfaceRequestor, michael@0: nsITransportEventSink, michael@0: nsIRequestObserver, michael@0: nsIStreamListener, michael@0: nsIAsyncVerifyRedirectCallback, michael@0: nsIPrivateBrowsingChannel) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel::nsIRequest michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetName(nsACString &result) michael@0: { michael@0: if (!mURI) { michael@0: result.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: return mURI->GetSpec(result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::IsPending(bool *result) michael@0: { michael@0: *result = Pending(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetStatus(nsresult *status) michael@0: { michael@0: if (mPump && NS_SUCCEEDED(mStatus)) { michael@0: mPump->GetStatus(status); michael@0: } else { michael@0: *status = mStatus; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::Cancel(nsresult status) michael@0: { michael@0: // Ignore redundant cancelation michael@0: if (NS_FAILED(mStatus)) michael@0: return NS_OK; michael@0: michael@0: mStatus = status; michael@0: michael@0: if (mPump) michael@0: mPump->Cancel(status); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::Suspend() michael@0: { michael@0: NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED); michael@0: return mPump->Suspend(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::Resume() michael@0: { michael@0: NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED); michael@0: return mPump->Resume(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) michael@0: { michael@0: *aLoadFlags = mLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) michael@0: { michael@0: mLoadFlags = aLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) michael@0: { michael@0: NS_IF_ADDREF(*aLoadGroup = mLoadGroup); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) michael@0: { michael@0: if (!CanSetLoadGroup(aLoadGroup)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mLoadGroup = aLoadGroup; michael@0: CallbacksChanged(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel::nsIChannel michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetOriginalURI(nsIURI **aURI) michael@0: { michael@0: *aURI = OriginalURI(); michael@0: NS_ADDREF(*aURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetOriginalURI(nsIURI *aURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: mOriginalURI = aURI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetURI(nsIURI **aURI) michael@0: { michael@0: NS_IF_ADDREF(*aURI = mURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetOwner(nsISupports **aOwner) michael@0: { michael@0: NS_IF_ADDREF(*aOwner = mOwner); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetOwner(nsISupports *aOwner) michael@0: { michael@0: mOwner = aOwner; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) michael@0: { michael@0: NS_IF_ADDREF(*aCallbacks = mCallbacks); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) michael@0: { michael@0: if (!CanSetCallbacks(aCallbacks)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mCallbacks = aCallbacks; michael@0: CallbacksChanged(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo) michael@0: { michael@0: NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetContentType(nsACString &aContentType) michael@0: { michael@0: aContentType = mContentType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetContentType(const nsACString &aContentType) michael@0: { michael@0: // mContentCharset is unchanged if not parsed michael@0: bool dummy; michael@0: net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetContentCharset(nsACString &aContentCharset) michael@0: { michael@0: aContentCharset = mContentCharset; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetContentCharset(const nsACString &aContentCharset) michael@0: { michael@0: mContentCharset = aContentCharset; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetContentDisposition(uint32_t *aContentDisposition) michael@0: { michael@0: // preserve old behavior, fail unless explicitly set. michael@0: if (mContentDispositionHint == UINT32_MAX) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: *aContentDisposition = mContentDispositionHint; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetContentDisposition(uint32_t aContentDisposition) michael@0: { michael@0: mContentDispositionHint = aContentDisposition; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) michael@0: { michael@0: if (!mContentDispositionFilename) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: aContentDispositionFilename = *mContentDispositionFilename; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) michael@0: { michael@0: mContentDispositionFilename = new nsString(aContentDispositionFilename); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetContentLength(int64_t *aContentLength) michael@0: { michael@0: *aContentLength = mContentLength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::SetContentLength(int64_t aContentLength) michael@0: { michael@0: mContentLength = aContentLength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::Open(nsIInputStream **result) michael@0: { michael@0: NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); michael@0: michael@0: nsCOMPtr chan; michael@0: nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan)); michael@0: NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?"); michael@0: if (NS_SUCCEEDED(rv) && chan) { michael@0: rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = chan->Open(result); michael@0: } else if (rv == NS_ERROR_NOT_IMPLEMENTED) michael@0: return NS_ImplementChannelOpen(this, result); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mWasOpened = true; michael@0: ClassifyURI(); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) michael@0: { michael@0: NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); michael@0: NS_ENSURE_ARG(listener); michael@0: michael@0: // Ensure that this is an allowed port before proceeding. michael@0: nsresult rv = NS_CheckPortSafety(mURI); michael@0: if (NS_FAILED(rv)) { michael@0: mCallbacks = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: // Store the listener and context early so that OpenContentStream and the michael@0: // stream's AsyncWait method (called by AsyncRead) can have access to them michael@0: // via PushStreamConverter and the StreamListener methods. However, since michael@0: // this typically introduces a reference cycle between this and the listener, michael@0: // we need to be sure to break the reference if this method does not succeed. michael@0: mListener = listener; michael@0: mListenerContext = ctxt; michael@0: michael@0: // This method assigns mPump as a side-effect. We need to clear mPump if michael@0: // this method fails. michael@0: rv = BeginPumpingData(); michael@0: if (NS_FAILED(rv)) { michael@0: mPump = nullptr; michael@0: ChannelDone(); michael@0: mCallbacks = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: // At this point, we are going to return success no matter what. michael@0: michael@0: mWasOpened = true; michael@0: michael@0: SUSPEND_PUMP_FOR_SCOPE(); michael@0: michael@0: if (mLoadGroup) michael@0: mLoadGroup->AddRequest(this, nullptr); michael@0: michael@0: ClassifyURI(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel::nsITransportEventSink michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status, michael@0: uint64_t progress, uint64_t progressMax) michael@0: { michael@0: // In some cases, we may wish to suppress transport-layer status events. michael@0: michael@0: if (!mPump || NS_FAILED(mStatus) || HasLoadFlag(LOAD_BACKGROUND)) michael@0: return NS_OK; michael@0: michael@0: SUSPEND_PUMP_FOR_SCOPE(); michael@0: michael@0: // Lazily fetch mProgressSink michael@0: if (!mProgressSink) { michael@0: if (mQueriedProgressSink) michael@0: return NS_OK; michael@0: GetCallback(mProgressSink); michael@0: mQueriedProgressSink = true; michael@0: if (!mProgressSink) michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString statusArg; michael@0: if (GetStatusArg(status, statusArg)) michael@0: mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get()); michael@0: michael@0: if (progress) michael@0: mProgressSink->OnProgress(this, mListenerContext, progress, progressMax); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel::nsIInterfaceRequestor michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::GetInterface(const nsIID &iid, void **result) michael@0: { michael@0: NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result); michael@0: return *result ? NS_OK : NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel::nsIRequestObserver michael@0: michael@0: static void michael@0: CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount) michael@0: { michael@0: nsIChannel *chan = static_cast(aClosure); michael@0: michael@0: nsAutoCString newType; michael@0: NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType); michael@0: if (!newType.IsEmpty()) { michael@0: chan->SetContentType(newType); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount) michael@0: { michael@0: nsIChannel *chan = static_cast(aClosure); michael@0: michael@0: nsCOMPtr sniffer = michael@0: do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER); michael@0: if (!sniffer) michael@0: return; michael@0: michael@0: nsAutoCString detected; michael@0: nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected); michael@0: if (NS_SUCCEEDED(rv)) michael@0: chan->SetContentType(detected); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) michael@0: { michael@0: // If our content type is unknown or if the content type is michael@0: // application/octet-stream and the caller requested it, use the content type michael@0: // sniffer. If the sniffer is not available for some reason, then we just keep michael@0: // going as-is. michael@0: bool shouldSniff = mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) || michael@0: ((mLoadFlags & LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN) && michael@0: mContentType.EqualsLiteral(APPLICATION_OCTET_STREAM)); michael@0: michael@0: if (NS_SUCCEEDED(mStatus) && shouldSniff) { michael@0: mPump->PeekStream(CallUnknownTypeSniffer, static_cast(this)); michael@0: } michael@0: michael@0: // Now, the general type sniffers. Skip this if we have none. michael@0: if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) michael@0: mPump->PeekStream(CallTypeSniffers, static_cast(this)); michael@0: michael@0: SUSPEND_PUMP_FOR_SCOPE(); michael@0: michael@0: if (mListener) // null in case of redirect michael@0: return mListener->OnStartRequest(this, mListenerContext); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, michael@0: nsresult status) michael@0: { michael@0: // If both mStatus and status are failure codes, we keep mStatus as-is since michael@0: // that is consistent with our GetStatus and Cancel methods. michael@0: if (NS_SUCCEEDED(mStatus)) michael@0: mStatus = status; michael@0: michael@0: // Cause Pending to return false. michael@0: mPump = nullptr; michael@0: michael@0: if (mListener) // null in case of redirect michael@0: mListener->OnStopRequest(this, mListenerContext, mStatus); michael@0: ChannelDone(); michael@0: michael@0: // No need to suspend pump in this scope since we will not be receiving michael@0: // any more events from it. michael@0: michael@0: if (mLoadGroup) michael@0: mLoadGroup->RemoveRequest(this, nullptr, mStatus); michael@0: michael@0: // Drop notification callbacks to prevent cycles. michael@0: mCallbacks = nullptr; michael@0: CallbacksChanged(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBaseChannel::nsIStreamListener michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, michael@0: nsIInputStream *stream, uint64_t offset, michael@0: uint32_t count) michael@0: { michael@0: SUSPEND_PUMP_FOR_SCOPE(); michael@0: michael@0: nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream, michael@0: offset, count); michael@0: if (mSynthProgressEvents && NS_SUCCEEDED(rv)) { michael@0: uint64_t prog = offset + count; michael@0: OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseChannel::OnRedirectVerifyCallback(nsresult result) michael@0: { michael@0: if (NS_SUCCEEDED(result)) michael@0: result = ContinueRedirect(); michael@0: michael@0: if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) { michael@0: if (NS_SUCCEEDED(mStatus)) michael@0: mStatus = result; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mWaitingOnAsyncRedirect) michael@0: ContinueHandleAsyncRedirect(result); michael@0: michael@0: return NS_OK; michael@0: }