From 6c7a1a881c4cd6057b08ce434af5495af74eb3bd Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 29 Sep 2008 17:02:44 -0400 Subject: [PATCH] Bug 455311. Treat .url files as redirects. r+sr=biesi --- netwerk/base/src/nsBaseChannel.cpp | 56 ++++++++++++++++--- netwerk/base/src/nsBaseChannel.h | 47 +++++++++++++--- netwerk/base/src/nsInputStreamChannel.cpp | 3 +- netwerk/base/src/nsInputStreamChannel.h | 3 +- netwerk/protocol/data/src/nsDataChannel.cpp | 3 +- netwerk/protocol/data/src/nsDataChannel.h | 3 +- netwerk/protocol/file/src/nsFileChannel.cpp | 21 ++++++- netwerk/protocol/file/src/nsFileChannel.h | 3 +- .../file/src/nsFileProtocolHandler.cpp | 20 +------ netwerk/protocol/ftp/src/nsFTPChannel.cpp | 3 +- netwerk/protocol/ftp/src/nsFTPChannel.h | 3 +- .../protocol/gopher/src/nsGopherChannel.cpp | 3 +- netwerk/protocol/gopher/src/nsGopherChannel.h | 3 +- 13 files changed, 127 insertions(+), 44 deletions(-) diff --git a/netwerk/base/src/nsBaseChannel.cpp b/netwerk/base/src/nsBaseChannel.cpp index fd5030e33ac..333ae29c5df 100644 --- a/netwerk/base/src/nsBaseChannel.cpp +++ b/netwerk/base/src/nsBaseChannel.cpp @@ -62,7 +62,7 @@ class ScopedRequestSuspender { public: ScopedRequestSuspender(nsIRequest *request) : mRequest(request) { - if (NS_FAILED(mRequest->Suspend())) { + if (mRequest && NS_FAILED(mRequest->Suspend())) { NS_WARNING("Couldn't suspend pump"); mRequest = nsnull; } @@ -94,7 +94,8 @@ nsBaseChannel::nsBaseChannel() } nsresult -nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags) +nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags, + PRBool openNewChannel) { SUSPEND_PUMP_FOR_SCOPE(); @@ -147,9 +148,11 @@ nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags) // unaffected, so we defer tearing down our channel until we have succeeded // with the redirect. - rv = newChannel->AsyncOpen(mListener, mListenerContext); - if (NS_FAILED(rv)) - return rv; + if (openNewChannel) { + rv = newChannel->AsyncOpen(mListener, mListenerContext); + if (NS_FAILED(rv)) + return rv; + } // close down this channel Cancel(NS_BINDING_REDIRECTED); @@ -216,10 +219,17 @@ nsresult nsBaseChannel::BeginPumpingData() { nsCOMPtr stream; - nsresult rv = OpenContentStream(PR_TRUE, getter_AddRefs(stream)); + nsCOMPtr channel; + nsresult rv = OpenContentStream(PR_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) + return NS_DispatchToCurrentThread(new RedirectRunnable(this, channel)); + // By assigning mPump, we flag this channel as pending (see IsPending). 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) @@ -234,6 +244,29 @@ nsBaseChannel::BeginPumpingData() return rv; } +void +nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel) +{ + NS_ASSERTION(!mPump, "Shouldn't have gotten here"); + nsresult rv = Redirect(newChannel, nsIChannelEventSink::REDIRECT_INTERNAL, + PR_TRUE); + if (NS_FAILED(rv)) { + // Notify our consumer ourselves + Cancel(rv); + mListener->OnStartRequest(this, mListenerContext); + mListener->OnStopRequest(this, mListenerContext, mStatus); + mListener = nsnull; + mListenerContext = nsnull; + } + + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nsnull, mStatus); + + // Drop notification callbacks to prevent cycles. + mCallbacks = nsnull; + CallbacksChanged(); +} + //----------------------------------------------------------------------------- // nsBaseChannel::nsISupports @@ -451,8 +484,15 @@ nsBaseChannel::Open(nsIInputStream **result) NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); - nsresult rv = OpenContentStream(PR_FALSE, result); - if (rv == NS_ERROR_NOT_IMPLEMENTED) + nsCOMPtr chan; + nsresult rv = OpenContentStream(PR_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, PR_FALSE); + if (NS_FAILED(rv)) + return rv; + rv = chan->Open(result); + } else if (rv == NS_ERROR_NOT_IMPLEMENTED) return NS_ImplementChannelOpen(this, result); mWasOpened = NS_SUCCEEDED(rv); diff --git a/netwerk/base/src/nsBaseChannel.h b/netwerk/base/src/nsBaseChannel.h index 4ead1b88f98..14f840eabc9 100644 --- a/netwerk/base/src/nsBaseChannel.h +++ b/netwerk/base/src/nsBaseChannel.h @@ -52,6 +52,7 @@ #include "nsIInterfaceRequestor.h" #include "nsIProgressEventSink.h" #include "nsITransport.h" +#include "nsThreadUtils.h" //----------------------------------------------------------------------------- // nsBaseChannel is designed to be subclassed. The subclass is responsible for @@ -103,7 +104,12 @@ private: // need to implement ReadSegments. If async is false, this method may return // NS_ERROR_NOT_IMPLEMENTED to cause the basechannel to implement Open in // terms of AsyncOpen (see NS_ImplementChannelOpen). - virtual nsresult OpenContentStream(PRBool async, nsIInputStream **stream) = 0; + // 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(PRBool async, nsIInputStream **stream, + nsIChannel** channel) = 0; // The basechannel calls this method from its OnTransportStatus method to // determine whether to call nsIProgressEventSink::OnStatus in addition to @@ -126,12 +132,14 @@ public: // Methods provided for use by the derived class: // Redirect to another channel. This method takes care of notifying - // observers of this redirect as well as of opening the new channel. It also - // cancels |this| with the status code NS_BINDING_REDIRECTED. A failure - // return from this method means that the redirect could not be performed (no - // channel was opened; this channel wasn't canceled.) The redirectFlags - // parameter consists of the flag values defined on nsIChannelEventSink. - nsresult Redirect(nsIChannel *newChannel, PRUint32 redirectFlags); + // observers of this redirect as well as of opening the new channel, if asked + // to do so. It also cancels |this| with the status code + // NS_BINDING_REDIRECTED. A failure return from this method means that the + // redirect could not be performed (no channel was opened; this channel + // wasn't canceled.) The redirectFlags parameter consists of the flag values + // defined on nsIChannelEventSink. + nsresult Redirect(nsIChannel *newChannel, PRUint32 redirectFlags, + PRBool openNewChannel); // Tests whether a type hint was set. Subclasses can use this to decide // whether to call SetContentType. @@ -233,6 +241,31 @@ private: OnCallbacksChanged(); } + // Handle an async redirect callback. This will only be called if we + // returned success from AsyncOpen while posting a redirect runnable. + void HandleAsyncRedirect(nsIChannel* newChannel); + + class RedirectRunnable : public nsRunnable + { + public: + RedirectRunnable(nsBaseChannel* chan, nsIChannel* newChannel) + : mChannel(chan), mNewChannel(newChannel) + { + NS_PRECONDITION(newChannel, "Must have channel to redirect to"); + } + + NS_IMETHOD Run() + { + mChannel->HandleAsyncRedirect(mNewChannel); + return NS_OK; + } + + private: + nsRefPtr mChannel; + nsCOMPtr mNewChannel; + }; + friend class RedirectRunnable; + nsRefPtr mPump; nsCOMPtr mCallbacks; nsCOMPtr mProgressSink; diff --git a/netwerk/base/src/nsInputStreamChannel.cpp b/netwerk/base/src/nsInputStreamChannel.cpp index 0a32e675600..fd35b9d132a 100644 --- a/netwerk/base/src/nsInputStreamChannel.cpp +++ b/netwerk/base/src/nsInputStreamChannel.cpp @@ -41,7 +41,8 @@ // nsInputStreamChannel nsresult -nsInputStreamChannel::OpenContentStream(PRBool async, nsIInputStream **result) +nsInputStreamChannel::OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel) { NS_ENSURE_TRUE(mContentStream, NS_ERROR_NOT_INITIALIZED); diff --git a/netwerk/base/src/nsInputStreamChannel.h b/netwerk/base/src/nsInputStreamChannel.h index 7d374d5cacf..a3aad38f6b6 100644 --- a/netwerk/base/src/nsInputStreamChannel.h +++ b/netwerk/base/src/nsInputStreamChannel.h @@ -55,7 +55,8 @@ public: protected: virtual ~nsInputStreamChannel() {} - virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result); + virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel); private: nsCOMPtr mContentStream; diff --git a/netwerk/protocol/data/src/nsDataChannel.cpp b/netwerk/protocol/data/src/nsDataChannel.cpp index 106e51e843b..fa63c9fb785 100644 --- a/netwerk/protocol/data/src/nsDataChannel.cpp +++ b/netwerk/protocol/data/src/nsDataChannel.cpp @@ -51,7 +51,8 @@ #include "prmem.h" nsresult -nsDataChannel::OpenContentStream(PRBool async, nsIInputStream **result) +nsDataChannel::OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel) { NS_ENSURE_TRUE(URI(), NS_ERROR_NOT_INITIALIZED); diff --git a/netwerk/protocol/data/src/nsDataChannel.h b/netwerk/protocol/data/src/nsDataChannel.h index c6534a3b917..5543be9201c 100644 --- a/netwerk/protocol/data/src/nsDataChannel.h +++ b/netwerk/protocol/data/src/nsDataChannel.h @@ -52,7 +52,8 @@ public: } protected: - virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result); + virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel); }; #endif /* nsDataChannel_h___ */ diff --git a/netwerk/protocol/file/src/nsFileChannel.cpp b/netwerk/protocol/file/src/nsFileChannel.cpp index 7513f44703e..2fff61d9f88 100644 --- a/netwerk/protocol/file/src/nsFileChannel.cpp +++ b/netwerk/protocol/file/src/nsFileChannel.cpp @@ -307,7 +307,8 @@ nsFileChannel::MakeFileInputStream(nsIFile *file, } nsresult -nsFileChannel::OpenContentStream(PRBool async, nsIInputStream **result) +nsFileChannel::OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel) { // NOTE: the resulting file is a clone, so it is safe to pass it to the // file input stream which will be read on a background thread. @@ -316,6 +317,24 @@ nsFileChannel::OpenContentStream(PRBool async, nsIInputStream **result) if (NS_FAILED(rv)) return rv; + nsCOMPtr fileHandler; + rv = NS_GetFileProtocolHandler(getter_AddRefs(fileHandler)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr newURI; + rv = fileHandler->ReadURLFile(file, getter_AddRefs(newURI)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr newChannel; + rv = NS_NewChannel(getter_AddRefs(newChannel), newURI); + if (NS_FAILED(rv)) + return rv; + + *result = nsnull; + newChannel.forget(channel); + return NS_OK; + } + nsCOMPtr stream; if (mUploadStream) { diff --git a/netwerk/protocol/file/src/nsFileChannel.h b/netwerk/protocol/file/src/nsFileChannel.h index cc559abd563..be9cbfbc622 100644 --- a/netwerk/protocol/file/src/nsFileChannel.h +++ b/netwerk/protocol/file/src/nsFileChannel.h @@ -65,7 +65,8 @@ protected: nsresult MakeFileInputStream(nsIFile *file, nsCOMPtr &stream, nsCString &contentType); - virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result); + virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel); private: nsCOMPtr mUploadStream; diff --git a/netwerk/protocol/file/src/nsFileProtocolHandler.cpp b/netwerk/protocol/file/src/nsFileProtocolHandler.cpp index 29f5fcf317d..b7189a1b9aa 100644 --- a/netwerk/protocol/file/src/nsFileProtocolHandler.cpp +++ b/netwerk/protocol/file/src/nsFileProtocolHandler.cpp @@ -278,30 +278,12 @@ nsFileProtocolHandler::NewURI(const nsACString &spec, NS_IMETHODIMP nsFileProtocolHandler::NewChannel(nsIURI *uri, nsIChannel **result) { - nsresult rv; - - // This file may be a url file - nsCOMPtr url(do_QueryInterface(uri)); - if (url) { - nsCOMPtr file; - rv = url->GetFile(getter_AddRefs(file)); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr uri; - rv = ReadURLFile(file, getter_AddRefs(uri)); - if (NS_SUCCEEDED(rv)) { - rv = NS_NewChannel(result, uri); - if (NS_SUCCEEDED(rv)) - return rv; - } - } - } - nsFileChannel *chan = new nsFileChannel(uri); if (!chan) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(chan); - rv = chan->Init(); + nsresult rv = chan->Init(); if (NS_FAILED(rv)) { NS_RELEASE(chan); return rv; diff --git a/netwerk/protocol/ftp/src/nsFTPChannel.cpp b/netwerk/protocol/ftp/src/nsFTPChannel.cpp index 688ebcb0386..7bc47431fa5 100644 --- a/netwerk/protocol/ftp/src/nsFTPChannel.cpp +++ b/netwerk/protocol/ftp/src/nsFTPChannel.cpp @@ -143,7 +143,8 @@ nsFtpChannel::GetProxyInfo(nsIProxyInfo** aProxyInfo) //----------------------------------------------------------------------------- nsresult -nsFtpChannel::OpenContentStream(PRBool async, nsIInputStream **result) +nsFtpChannel::OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel) { if (!async) return NS_ERROR_NOT_IMPLEMENTED; diff --git a/netwerk/protocol/ftp/src/nsFTPChannel.h b/netwerk/protocol/ftp/src/nsFTPChannel.h index bec646460a9..314337e2952 100644 --- a/netwerk/protocol/ftp/src/nsFTPChannel.h +++ b/netwerk/protocol/ftp/src/nsFTPChannel.h @@ -117,7 +117,8 @@ public: protected: virtual ~nsFtpChannel() {} - virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result); + virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel); virtual PRBool GetStatusArg(nsresult status, nsString &statusArg); virtual void OnCallbacksChanged(); diff --git a/netwerk/protocol/gopher/src/nsGopherChannel.cpp b/netwerk/protocol/gopher/src/nsGopherChannel.cpp index d3fd802f6f1..317d73ba70a 100644 --- a/netwerk/protocol/gopher/src/nsGopherChannel.cpp +++ b/netwerk/protocol/gopher/src/nsGopherChannel.cpp @@ -469,7 +469,8 @@ nsGopherChannel::GetProxyInfo(nsIProxyInfo** aProxyInfo) } nsresult -nsGopherChannel::OpenContentStream(PRBool async, nsIInputStream **result) +nsGopherChannel::OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel) { // Implement nsIChannel::Open in terms of nsIChannel::AsyncOpen if (!async) diff --git a/netwerk/protocol/gopher/src/nsGopherChannel.h b/netwerk/protocol/gopher/src/nsGopherChannel.h index 4cfe9d8f5a0..ba9bccacaa5 100644 --- a/netwerk/protocol/gopher/src/nsGopherChannel.h +++ b/netwerk/protocol/gopher/src/nsGopherChannel.h @@ -59,7 +59,8 @@ public: protected: virtual ~nsGopherChannel() {} - virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result); + virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result, + nsIChannel** channel); virtual PRBool GetStatusArg(nsresult status, nsString &statusArg); private: