--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -37,26 +37,27 @@ public:
~ScopedRequestSuspender() {
if (mRequest)
mRequest->Resume();
}
private:
nsIRequest *mRequest;
};
-// Used to suspend data events from mPump within a function scope. This is
+// Used to suspend data events from mRequest within a function scope. This is
// usually needed when a function makes callbacks that could process events.
#define SUSPEND_PUMP_FOR_SCOPE() \
- ScopedRequestSuspender pump_suspender__(mPump)
+ ScopedRequestSuspender pump_suspender__(mRequest)
//-----------------------------------------------------------------------------
// nsBaseChannel
nsBaseChannel::nsBaseChannel()
- : mLoadFlags(LOAD_NORMAL)
+ : mPumpingData(false)
+ , mLoadFlags(LOAD_NORMAL)
, mQueriedProgressSink(true)
, mSynthProgressEvents(false)
, mAllowThreadRetargeting(true)
, mWaitingOnAsyncRedirect(false)
, mOpenRedirectChannel(false)
, mStatus(NS_OK)
, mContentDispositionHint(UINT32_MAX)
, mContentLength(-1)
@@ -226,20 +227,28 @@ nsBaseChannel::PushStreamConverter(const
}
}
return rv;
}
nsresult
nsBaseChannel::BeginPumpingData()
{
+ nsresult rv = BeginAsyncRead(this, getter_AddRefs(mRequest));
+ if (NS_SUCCEEDED(rv)) {
+ mPumpingData = true;
+ return rv;
+ }
+ if (rv != NS_ERROR_NOT_IMPLEMENTED)
+ return rv;
+
nsCOMPtr<nsIInputStream> stream;
nsCOMPtr<nsIChannel> channel;
- nsresult rv = OpenContentStream(true, getter_AddRefs(stream),
- getter_AddRefs(channel));
+ rv = OpenContentStream(true, getter_AddRefs(stream),
+ getter_AddRefs(channel));
if (NS_FAILED(rv))
return rv;
NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?");
if (channel) {
rv = NS_DispatchToCurrentThread(new RedirectRunnable(this, channel));
if (NS_SUCCEEDED(rv))
@@ -250,26 +259,29 @@ nsBaseChannel::BeginPumpingData()
// By assigning mPump, we flag this channel as pending (see Pending). It's
// important that the pending flag is set when we call into the stream (the
// call to AsyncRead results in the stream's AsyncWait method being called)
// and especially when we call into the loadgroup. Our caller takes care to
// release mPump if we return an error.
rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0,
true);
- if (NS_SUCCEEDED(rv))
+ if (NS_SUCCEEDED(rv)) {
+ mPumpingData = true;
+ mRequest = mPump;
rv = mPump->AsyncRead(this, nullptr);
+ }
return rv;
}
void
nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
{
- NS_ASSERTION(!mPump, "Shouldn't have gotten here");
+ NS_ASSERTION(!mPumpingData, "Shouldn't have gotten here");
nsresult rv = mStatus;
if (NS_SUCCEEDED(mStatus)) {
rv = Redirect(newChannel,
nsIChannelEventSink::REDIRECT_TEMPORARY,
true);
if (NS_SUCCEEDED(rv)) {
// OnRedirectVerifyCallback will be called asynchronously
@@ -356,51 +368,53 @@ nsBaseChannel::IsPending(bool *result)
{
*result = Pending();
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::GetStatus(nsresult *status)
{
- if (mPump && NS_SUCCEEDED(mStatus)) {
- mPump->GetStatus(status);
+ if (mRequest && NS_SUCCEEDED(mStatus)) {
+ mRequest->GetStatus(status);
} else {
*status = mStatus;
}
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::Cancel(nsresult status)
{
// Ignore redundant cancelation
if (NS_FAILED(mStatus))
return NS_OK;
mStatus = status;
- if (mPump)
- mPump->Cancel(status);
+ if (mRequest)
+ mRequest->Cancel(status);
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::Suspend()
{
- NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
- return mPump->Suspend();
+ NS_ENSURE_TRUE(mPumpingData, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_IMPLEMENTED);
+ return mRequest->Suspend();
}
NS_IMETHODIMP
nsBaseChannel::Resume()
{
- NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
- return mPump->Resume();
+ NS_ENSURE_TRUE(mPumpingData, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_IMPLEMENTED);
+ return mRequest->Resume();
}
NS_IMETHODIMP
nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
{
*aLoadFlags = mLoadFlags;
return NS_OK;
}
@@ -599,17 +613,17 @@ nsBaseChannel::SetContentLength(int64_t
mContentLength = aContentLength;
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::Open(nsIInputStream **result)
{
NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
+ NS_ENSURE_TRUE(!mPumpingData, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
nsCOMPtr<nsIChannel> chan;
nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan));
NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?");
if (NS_SUCCEEDED(rv) && chan) {
rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false);
if (NS_FAILED(rv))
@@ -641,17 +655,17 @@ nsBaseChannel::AsyncOpen(nsIStreamListen
MOZ_ASSERT(!mLoadInfo ||
mLoadInfo->GetSecurityMode() == 0 ||
mLoadInfo->GetInitialSecurityCheckDone() ||
(mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
"security flags in loadInfo but asyncOpen2() not called");
NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
+ NS_ENSURE_TRUE(!mPumpingData, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
NS_ENSURE_ARG(listener);
// Skip checking for chrome:// sub-resources.
nsAutoCString scheme;
mURI->GetScheme(scheme);
if (!scheme.EqualsLiteral("file")) {
NS_CompareLoadInfoAndLoadContext(this);
@@ -672,16 +686,18 @@ nsBaseChannel::AsyncOpen(nsIStreamListen
mListener = listener;
mListenerContext = ctxt;
// This method assigns mPump as a side-effect. We need to clear mPump if
// this method fails.
rv = BeginPumpingData();
if (NS_FAILED(rv)) {
mPump = nullptr;
+ mRequest = nullptr;
+ mPumpingData = false;
ChannelDone();
mCallbacks = nullptr;
return rv;
}
// At this point, we are going to return success no matter what.
mWasOpened = true;
@@ -712,17 +728,17 @@ nsBaseChannel::AsyncOpen2(nsIStreamListe
// nsBaseChannel::nsITransportEventSink
NS_IMETHODIMP
nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status,
int64_t progress, int64_t progressMax)
{
// In some cases, we may wish to suppress transport-layer status events.
- if (!mPump || NS_FAILED(mStatus)) {
+ if (!mPumpingData || NS_FAILED(mStatus)) {
return NS_OK;
}
SUSPEND_PUMP_FOR_SCOPE();
// Lazily fetch mProgressSink
if (!mProgressSink) {
if (mQueriedProgressSink) {
@@ -788,30 +804,32 @@ CallUnknownTypeSniffer(void *aClosure, c
nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected);
if (NS_SUCCEEDED(rv))
chan->SetContentType(detected);
}
NS_IMETHODIMP
nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
- MOZ_ASSERT(request == mPump);
+ MOZ_ASSERT_IF(mRequest, request == mRequest);
- // If our content type is unknown, use the content type
- // sniffer. If the sniffer is not available for some reason, then we just keep
- // going as-is.
- if (NS_SUCCEEDED(mStatus) &&
- mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
- mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this));
+ if (mPump) {
+ // If our content type is unknown, use the content type
+ // sniffer. If the sniffer is not available for some reason, then we just keep
+ // going as-is.
+ if (NS_SUCCEEDED(mStatus) &&
+ mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
+ mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this));
+ }
+
+ // Now, the general type sniffers. Skip this if we have none.
+ if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS)
+ mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
}
- // Now, the general type sniffers. Skip this if we have none.
- if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS)
- mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
-
SUSPEND_PUMP_FOR_SCOPE();
if (mListener) // null in case of redirect
return mListener->OnStartRequest(this, mListenerContext);
return NS_OK;
}
NS_IMETHODIMP
@@ -820,16 +838,18 @@ nsBaseChannel::OnStopRequest(nsIRequest
{
// If both mStatus and status are failure codes, we keep mStatus as-is since
// that is consistent with our GetStatus and Cancel methods.
if (NS_SUCCEEDED(mStatus))
mStatus = status;
// Cause Pending to return false.
mPump = nullptr;
+ mRequest = nullptr;
+ mPumpingData = false;
if (mListener) // null in case of redirect
mListener->OnStopRequest(this, mListenerContext, mStatus);
ChannelDone();
// No need to suspend pump in this scope since we will not be receiving
// any more events from it.
@@ -908,23 +928,26 @@ nsBaseChannel::OnRedirectVerifyCallback(
return NS_OK;
}
NS_IMETHODIMP
nsBaseChannel::RetargetDeliveryTo(nsIEventTarget* aEventTarget)
{
MOZ_ASSERT(NS_IsMainThread());
- NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_INITIALIZED);
- if (!mAllowThreadRetargeting) {
- return NS_ERROR_NOT_IMPLEMENTED;
+ nsCOMPtr<nsIThreadRetargetableRequest> req;
+ if (mAllowThreadRetargeting) {
+ req = do_QueryInterface(mRequest);
}
- return mPump->RetargetDeliveryTo(aEventTarget);
+ NS_ENSURE_TRUE(req, NS_ERROR_NOT_IMPLEMENTED);
+
+ return req->RetargetDeliveryTo(aEventTarget);
}
NS_IMETHODIMP
nsBaseChannel::CheckListenerChain()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAllowThreadRetargeting) {
--- a/netwerk/base/nsBaseChannel.h
+++ b/netwerk/base/nsBaseChannel.h
@@ -87,16 +87,29 @@ private:
// terms of AsyncOpen (see NS_ImplementChannelOpen).
// A callee is allowed to return an nsIChannel instead of an nsIInputStream.
// That case will be treated as a redirect to the new channel. By default
// *channel will be set to null by the caller, so callees who don't want to
// return one an just not touch it.
virtual nsresult OpenContentStream(bool async, nsIInputStream **stream,
nsIChannel** channel) = 0;
+ // Implemented by subclass to begin pumping data for an async channel, in
+ // lieu of returning a stream. If implemented, OpenContentStream will never
+ // be called for async channels. If not implemented, AsyncOpen2 will fall
+ // back to OpenContentStream.
+ //
+ // On success, the callee must begin pumping data to the stream listener,
+ // and at some point call OnStartRequest followed by OnStopRequest.
+ // Additionally, it may provide a request object which may be used to
+ // suspend, resume, and cancel the underlying request.
+ virtual nsresult BeginAsyncRead(nsIStreamListener* listener, nsIRequest** request) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
// The basechannel calls this method from its OnTransportStatus method to
// determine whether to call nsIProgressEventSink::OnStatus in addition to
// nsIProgressEventSink::OnProgress. This method may be overriden by the
// subclass to enable nsIProgressEventSink::OnStatus events. If this method
// returns true, then the statusArg out param specifies the "statusArg" value
// to pass to the OnStatus method. By default, OnStatus messages are
// suppressed. The status parameter passed to this method is the status value
// from the OnTransportStatus method.
@@ -159,17 +172,17 @@ public:
// Test the load flags
bool HasLoadFlag(uint32_t flag) {
return (mLoadFlags & flag) != 0;
}
// This is a short-cut to calling nsIRequest::IsPending()
virtual bool Pending() const {
- return mPump || mWaitingOnAsyncRedirect;
+ return mPumpingData || mWaitingOnAsyncRedirect;
}
// Helper function for querying the channel's notification callbacks.
template <class T> void GetCallback(nsCOMPtr<T> &result) {
GetInterface(NS_GET_TEMPLATE_IID(T), getter_AddRefs(result));
}
// Helper function for calling QueryInterface on this.
@@ -261,16 +274,18 @@ private:
private:
RefPtr<nsBaseChannel> mChannel;
nsCOMPtr<nsIChannel> mNewChannel;
};
friend class RedirectRunnable;
RefPtr<nsInputStreamPump> mPump;
+ RefPtr<nsIRequest> mRequest;
+ bool mPumpingData;
nsCOMPtr<nsIProgressEventSink> mProgressSink;
nsCOMPtr<nsIURI> mOriginalURI;
nsCOMPtr<nsISupports> mOwner;
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCString mContentType;
nsCString mContentCharset;
uint32_t mLoadFlags;