Merge.
authorRobert Sayre <sayrer@gmail.com>
Mon, 06 Oct 2008 16:55:12 -0400
changeset 20093 6ecedbcf4d050047b78a04cf778f0c190858320c
parent 20092 4d331883f18d0c60463c5a9c2fe49e8f6fd1b02b (current diff)
parent 20091 e3b597862e2a2d0f6556f7a6a439e1b1ee487521 (diff)
child 20094 ce56bc915463fb0fad704c6d3893a4ea1c7cd1a6
child 20181 dd18104921a94fe82e6d2638980038c7330c86b1
push idunknown
push userunknown
push dateunknown
milestone1.9.1b1pre
Merge.
--- a/netwerk/base/src/nsBaseChannel.cpp
+++ b/netwerk/base/src/nsBaseChannel.cpp
@@ -57,17 +57,17 @@ CopyProperties(const nsAString &key, nsI
   return PL_DHASH_NEXT;
 }
 
 // This class is used to suspend a request across a function scope.
 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;
     }
   }
   ~ScopedRequestSuspender() {
     if (mRequest)
       mRequest->Resume();
   }
@@ -89,17 +89,18 @@ nsBaseChannel::nsBaseChannel()
   , mQueriedProgressSink(PR_TRUE)
   , mSynthProgressEvents(PR_FALSE)
   , mWasOpened(PR_FALSE)
 {
   mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
 }
 
 nsresult
-nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags)
+nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags,
+                        PRBool openNewChannel)
 {
   SUSPEND_PUMP_FOR_SCOPE();
 
   // Transfer properties
 
   newChannel->SetOriginalURI(OriginalURI());
   newChannel->SetLoadGroup(mLoadGroup);
   newChannel->SetNotificationCallbacks(mCallbacks);
@@ -142,19 +143,21 @@ nsBaseChannel::Redirect(nsIChannel *newC
     if (NS_FAILED(rv))
       return rv;
   }
 
   // If we fail to open the new channel, then we want to leave this channel
   // 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);
   mListener = nsnull;
   mListenerContext = nsnull;
 
   return NS_OK;
 }
@@ -211,34 +214,64 @@ nsBaseChannel::PushStreamConverter(const
   }
   return rv;
 }
 
 nsresult
 nsBaseChannel::BeginPumpingData()
 {
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = OpenContentStream(PR_TRUE, getter_AddRefs(stream));
+  nsCOMPtr<nsIChannel> 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)
   // 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,
                                  PR_TRUE);
   if (NS_SUCCEEDED(rv))
     rv = mPump->AsyncRead(this, nsnull);
 
   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
 
 NS_IMPL_ISUPPORTS_INHERITED6(nsBaseChannel,
                              nsHashPropertyBag,
                              nsIRequest,
                              nsIChannel,
                              nsIInterfaceRequestor,
@@ -446,18 +479,25 @@ nsBaseChannel::SetContentLength(PRInt32 
 
 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(!mWasOpened, NS_ERROR_IN_PROGRESS);
 
-  nsresult rv = OpenContentStream(PR_FALSE, result);
-  if (rv == NS_ERROR_NOT_IMPLEMENTED)
+  nsCOMPtr<nsIChannel> 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);
 
   return rv;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/src/nsBaseChannel.h
+++ b/netwerk/base/src/nsBaseChannel.h
@@ -47,16 +47,17 @@
 #include "nsIChannel.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsILoadGroup.h"
 #include "nsIStreamListener.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIProgressEventSink.h"
 #include "nsITransport.h"
+#include "nsThreadUtils.h"
 
 //-----------------------------------------------------------------------------
 // nsBaseChannel is designed to be subclassed.  The subclass is responsible for
 // implementing the OpenContentStream method, which will be called by the
 // nsIChannel::AsyncOpen and nsIChannel::Open implementations.
 //
 // nsBaseChannel implements nsIInterfaceRequestor to provide a convenient way
 // for subclasses to query both the nsIChannel::notificationCallbacks and
@@ -98,17 +99,22 @@ private:
   // instance.  This means that if it is a non-blocking stream that supports
   // nsIAsyncInputStream that it will be read entirely on the main application
   // thread, and its AsyncWait method will be called whenever ReadSegments
   // returns NS_BASE_STREAM_WOULD_BLOCK.  Otherwise, if the stream is blocking,
   // then it will be read on one of the background I/O threads, and it does not
   // 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
   // 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
@@ -121,22 +127,24 @@ private:
   virtual void OnCallbacksChanged() {
   }
 
 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.
   // NOTE: This is only reliable if the subclass didn't itself call
   // SetContentType, and should also not be called after OpenContentStream.
   PRBool HasContentTypeHint() const;
 
   // The URI member should be initialized before the channel is used, and then
@@ -228,16 +236,41 @@ private:
 
   // Called when the callbacks available to this channel may have changed.
   void CallbacksChanged() {
     mProgressSink = nsnull;
     mQueriedProgressSink = PR_FALSE;
     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<nsBaseChannel> mChannel;
+    nsCOMPtr<nsIChannel> mNewChannel;
+  };
+  friend class RedirectRunnable;
+
   nsRefPtr<nsInputStreamPump>         mPump;
   nsCOMPtr<nsIInterfaceRequestor>     mCallbacks;
   nsCOMPtr<nsIProgressEventSink>      mProgressSink;
   nsCOMPtr<nsIURI>                    mOriginalURI;
   nsCOMPtr<nsIURI>                    mURI;
   nsCOMPtr<nsILoadGroup>              mLoadGroup;
   nsCOMPtr<nsISupports>               mOwner;
   nsCOMPtr<nsISupports>               mSecurityInfo;
--- a/netwerk/base/src/nsInputStreamChannel.cpp
+++ b/netwerk/base/src/nsInputStreamChannel.cpp
@@ -36,17 +36,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsInputStreamChannel.h"
 
 //-----------------------------------------------------------------------------
 // nsInputStreamChannel
 
 nsresult
-nsInputStreamChannel::OpenContentStream(PRBool async, nsIInputStream **result)
+nsInputStreamChannel::OpenContentStream(PRBool async, nsIInputStream **result,
+                                        nsIChannel** channel)
 {
   NS_ENSURE_TRUE(mContentStream, NS_ERROR_NOT_INITIALIZED);
 
   // If content length is unknown, then we must guess.  In this case, we assume
   // the stream can tell us.  If the stream is a pipe, then this will not work.
 
   PRInt64 len = ContentLength64();
   if (len < 0) {
--- a/netwerk/base/src/nsInputStreamChannel.h
+++ b/netwerk/base/src/nsInputStreamChannel.h
@@ -50,15 +50,16 @@ public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIINPUTSTREAMCHANNEL
 
     nsInputStreamChannel() {}
 
 protected:
     virtual ~nsInputStreamChannel() {}
 
-    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                       nsIChannel** channel);
 
 private:
     nsCOMPtr<nsIInputStream> mContentStream;
 };
 
 #endif // !nsInputStreamChannel_h__
--- a/netwerk/protocol/data/src/nsDataChannel.cpp
+++ b/netwerk/protocol/data/src/nsDataChannel.cpp
@@ -46,17 +46,18 @@
 #include "nsReadableUtils.h"
 #include "nsNetSegmentUtils.h"
 #include "nsEscape.h"
 #include "plbase64.h"
 #include "plstr.h"
 #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);
 
     nsresult rv;
 
     nsCAutoString spec;
     rv = URI()->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) return rv;
--- a/netwerk/protocol/data/src/nsDataChannel.h
+++ b/netwerk/protocol/data/src/nsDataChannel.h
@@ -47,12 +47,13 @@
 
 class nsDataChannel : public nsBaseChannel {
 public:
     nsDataChannel(nsIURI *uri) {
         SetURI(uri);
     }
 
 protected:
-    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                       nsIChannel** channel);
 };
 
 #endif /* nsDataChannel_h___ */
--- a/netwerk/protocol/file/src/nsFileChannel.cpp
+++ b/netwerk/protocol/file/src/nsFileChannel.cpp
@@ -302,25 +302,44 @@ nsFileChannel::MakeFileInputStream(nsIFi
         mime->GetTypeFromFile(file, contentType);
       }
     }
   }
   return rv;
 }
 
 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.
   nsCOMPtr<nsIFile> file;
   nsresult rv = GetFile(getter_AddRefs(file));
   if (NS_FAILED(rv))
     return rv;
 
+  nsCOMPtr<nsIFileProtocolHandler> fileHandler;
+  rv = NS_GetFileProtocolHandler(getter_AddRefs(fileHandler));
+  if (NS_FAILED(rv))
+    return rv;
+    
+  nsCOMPtr<nsIURI> newURI;
+  rv = fileHandler->ReadURLFile(file, getter_AddRefs(newURI));
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIChannel> newChannel;
+    rv = NS_NewChannel(getter_AddRefs(newChannel), newURI);
+    if (NS_FAILED(rv))
+      return rv;
+
+    *result = nsnull;
+    newChannel.forget(channel);
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIInputStream> stream;
 
   if (mUploadStream) {
     // Pass back a nsFileUploadContentStream instance that knows how to perform
     // the file copy when "read" (the resulting stream in this case does not
     // actually return any data).
 
     nsCOMPtr<nsIOutputStream> fileStream;
--- a/netwerk/protocol/file/src/nsFileChannel.h
+++ b/netwerk/protocol/file/src/nsFileChannel.h
@@ -60,16 +60,17 @@ public:
 protected:
   // Called to construct a blocking file input stream for the given file.  This
   // method also returns a best guess at the content-type for the data stream.
   // NOTE: If the channel has a type hint set, contentType will be left
   // untouched. The caller should not use it in that case.
   nsresult MakeFileInputStream(nsIFile *file, nsCOMPtr<nsIInputStream> &stream,
                                nsCString &contentType);
 
-  virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+  virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                     nsIChannel** channel);
 
 private:
   nsCOMPtr<nsIInputStream> mUploadStream;
   PRInt64 mUploadLength;
 };
 
 #endif // !nsFileChannel_h__
--- a/netwerk/protocol/file/src/nsFileProtocolHandler.cpp
+++ b/netwerk/protocol/file/src/nsFileProtocolHandler.cpp
@@ -273,40 +273,22 @@ nsFileProtocolHandler::NewURI(const nsAC
     if (NS_FAILED(rv)) return rv;
 
     return CallQueryInterface(url, result);
 }
 
 NS_IMETHODIMP
 nsFileProtocolHandler::NewChannel(nsIURI *uri, nsIChannel **result)
 {
-    nsresult rv;
-
-    // This file may be a url file
-    nsCOMPtr<nsIFileURL> url(do_QueryInterface(uri));
-    if (url) {
-        nsCOMPtr<nsIFile> file;
-        rv = url->GetFile(getter_AddRefs(file));
-        if (NS_SUCCEEDED(rv)) {
-            nsCOMPtr<nsIURI> 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;
     }
 
     *result = chan;
     return NS_OK;
 }
--- a/netwerk/protocol/ftp/src/nsFTPChannel.cpp
+++ b/netwerk/protocol/ftp/src/nsFTPChannel.cpp
@@ -138,17 +138,18 @@ nsFtpChannel::GetProxyInfo(nsIProxyInfo*
     *aProxyInfo = ProxyInfo();
     NS_IF_ADDREF(*aProxyInfo);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 nsresult
-nsFtpChannel::OpenContentStream(PRBool async, nsIInputStream **result)
+nsFtpChannel::OpenContentStream(PRBool async, nsIInputStream **result,
+                                nsIChannel** channel)
 {
     if (!async)
         return NS_ERROR_NOT_IMPLEMENTED;
 
     nsFtpState *state = new nsFtpState();
     if (!state)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(state);
--- a/netwerk/protocol/ftp/src/nsFTPChannel.h
+++ b/netwerk/protocol/ftp/src/nsFTPChannel.h
@@ -112,17 +112,18 @@ public:
         return mUploadStream;
     }
 
     // Helper function for getting the nsIFTPEventSink.
     void GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult);
 
 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();
 
 private:
     nsCOMPtr<nsIProxyInfo>    mProxyInfo; 
     nsCOMPtr<nsIFTPEventSink> mFTPEventSink;
     nsCOMPtr<nsIInputStream>  mUploadStream;
     PRUint64                  mStartPos;
--- a/netwerk/protocol/gopher/src/nsGopherChannel.cpp
+++ b/netwerk/protocol/gopher/src/nsGopherChannel.cpp
@@ -464,17 +464,18 @@ NS_IMETHODIMP
 nsGopherChannel::GetProxyInfo(nsIProxyInfo** aProxyInfo)
 {
     *aProxyInfo = ProxyInfo();
     NS_IF_ADDREF(*aProxyInfo);
     return NS_OK;
 }
 
 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)
         return NS_ERROR_NOT_IMPLEMENTED;
 
     nsRefPtr<nsIInputStream> stream = new nsGopherContentStream(this);
     if (!stream)
         return NS_ERROR_OUT_OF_MEMORY;
--- a/netwerk/protocol/gopher/src/nsGopherChannel.h
+++ b/netwerk/protocol/gopher/src/nsGopherChannel.h
@@ -54,17 +54,18 @@ public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIPROXIEDCHANNEL
 
     nsIProxyInfo *ProxyInfo() { return mProxyInfo; }
 
 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:
     nsresult SendRequest(nsIOutputStream *stream);
 
     nsCOMPtr<nsIProxyInfo> mProxyInfo;
 };