Bug 536294 - e10s HTTP: redirects. r=jduell
authorHonza Bambas <honzab.moz@firemni.cz>
Tue, 10 Aug 2010 20:11:57 -0700
changeset 49375 18cd465199df7cc9fe5d64666162f3a569aeb268
parent 49367 14342400e38b822ae1fbd091031d380a98b3b98a
child 49376 adbf1571c30d04349e22a7a0cbb8151da0c872a0
push idunknown
push userunknown
push dateunknown
reviewersjduell
bugs536294
milestone2.0b4pre
Bug 536294 - e10s HTTP: redirects. r=jduell
netwerk/base/public/Makefile.in
netwerk/base/public/nsIRedirectResultListener.idl
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/HttpChannelParentListener.cpp
netwerk/protocol/http/HttpChannelParentListener.h
netwerk/protocol/http/Makefile.in
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/test/unit_ipc/test_event_sink_wrap.js
netwerk/test/unit_ipc/test_redirect-caching_canceled_wrap.js
netwerk/test/unit_ipc/test_redirect-caching_failure_wrap.js
netwerk/test/unit_ipc/test_redirect-caching_passing_wrap.js
netwerk/test/unit_ipc/test_redirect_canceled_wrap.js
netwerk/test/unit_ipc/test_redirect_failure_wrap.js
netwerk/test/unit_ipc/test_redirect_passing_wrap.js
--- a/netwerk/base/public/Makefile.in
+++ b/netwerk/base/public/Makefile.in
@@ -134,16 +134,17 @@ XPIDLSRCS	= \
 		nsIAuthPromptProvider.idl \
 		nsPISocketTransportService.idl \
 		nsIChannelEventSink.idl \
 		nsINetUtil.idl \
 		nsIProxiedChannel.idl \
 		nsIRandomGenerator.idl \
 		nsIURIWithPrincipal.idl \
 		nsIURIClassifier.idl \
+		nsIRedirectResultListener.idl \
 		$(NULL)
 
 EXPORTS		= \
 		netCore.h \
 		nsNetError.h \
 		nsNetUtil.h \
 		nsNetStrings.h \
 		nsChannelProperties.h \
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsIRedirectResultListener.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is nsIApplicationCache.idl.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Honza Bambas <honzab@firemni.cz>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(85cd2640-e91e-41ac-bdca-1dbf10dc131e)]
+interface nsIRedirectResultListener : nsISupports
+{
+  /**
+   *  When an HTTP redirect has been processed (either successfully or not)
+   *  nsIHttpChannel will call this function if its callbacks implement this
+   *  interface.
+   *
+   *  @param proceeding
+   *         Indicated whether the redirect will be proceeding, or not (i.e.
+   *         has been canceled, or failed).
+   */
+  void onRedirectResult(in PRBool proceeding);
+};
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -18,16 +18,17 @@
  *
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
+ *   Honza Bambas <honzab@firemni.cz>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -82,21 +83,27 @@ void NeckoChild::DestroyNeckoChild()
   if (!alreadyDestroyed) {
     Send__delete__(gNeckoChild); 
     gNeckoChild = nsnull;
     alreadyDestroyed = true;
   }
 }
 
 PHttpChannelChild* 
-NeckoChild::AllocPHttpChannel(PBrowserChild* iframeEmbedding)
+NeckoChild::AllocPHttpChannel(PBrowserChild* browser)
 {
-  // We don't allocate here: see HttpChannelChild::AsyncOpen()
-  NS_RUNTIMEABORT("AllocPHttpChannel should not be called");
-  return nsnull;
+  // This constructor is only used when PHttpChannel is constructed by
+  // the parent process, e.g. during a redirect.  (Normally HttpChannelChild is
+  // created by nsHttpHandler::NewProxiedChannel(), and then creates the
+  // PHttpChannel in HttpChannelChild::AsyncOpen().)
+
+  // No need to store PBrowser. It is only needed by the parent.
+  HttpChannelChild* httpChannel = new HttpChannelChild();
+  httpChannel->AddIPDLReference();
+  return httpChannel;
 }
 
 bool 
 NeckoChild::DeallocPHttpChannel(PHttpChannelChild* channel)
 {
   NS_ABORT_IF_FALSE(IsNeckoChild(), "DeallocPHttpChannel called by non-child!");
 
   HttpChannelChild* child = static_cast<HttpChannelChild*>(channel);
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -53,19 +53,19 @@ NeckoParent::NeckoParent()
 {
 }
 
 NeckoParent::~NeckoParent()
 {
 }
 
 PHttpChannelParent* 
-NeckoParent::AllocPHttpChannel(PBrowserParent* iframeEmbedding)
+NeckoParent::AllocPHttpChannel(PBrowserParent* browser)
 {
-  HttpChannelParent *p = new HttpChannelParent(iframeEmbedding);
+  HttpChannelParent *p = new HttpChannelParent(browser);
   p->AddRef();
   return p;
 }
 
 bool 
 NeckoParent::DeallocPHttpChannel(PHttpChannelParent* channel)
 {
   HttpChannelParent *p = static_cast<HttpChannelParent *>(channel);
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -51,17 +51,17 @@ namespace net {
 class NeckoParent :
   public PNeckoParent
 {
 public:
   NeckoParent();
   virtual ~NeckoParent();
 
 protected:
-  virtual PHttpChannelParent* AllocPHttpChannel(PBrowserParent* iframeEmbedding);
+  virtual PHttpChannelParent* AllocPHttpChannel(PBrowserParent* browser);
   virtual bool DeallocPHttpChannel(PHttpChannelParent*);
   virtual PCookieServiceParent* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceParent*);
   virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
                                    const PRUint16& flags);
 };
 
 } // namespace net
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -52,17 +52,19 @@ sync protocol PNecko
 {
   manager PContent;
   manages PHttpChannel;
   manages PCookieService;
 
 parent:
   __delete__();
 
-  PHttpChannel(nullable PBrowser iframeEmbedding);
   PCookieService();
 
   HTMLDNSPrefetch(nsString hostname, PRUint16 flags);
+
+both:
+  PHttpChannel(nullable PBrowser browser);
 };
 
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -41,32 +41,41 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/net/HttpBaseChannel.h"
 
 #include "nsHttpHandler.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 
+#include "nsICachingChannel.h"
+#include "nsISeekableStream.h"
+#include "nsIEncodedChannel.h"
+#include "nsIResumableChannel.h"
+#include "nsIApplicationCacheChannel.h"
+
 namespace mozilla {
 namespace net {
 
 HttpBaseChannel::HttpBaseChannel()
   : mStatus(NS_OK)
   , mLoadFlags(LOAD_NORMAL)
   , mPriority(PRIORITY_NORMAL)
   , mCaps(0)
   , mRedirectionLimit(gHttpHandler->RedirectionLimit())
   , mCanceled(PR_FALSE)
   , mIsPending(PR_FALSE)
   , mWasOpened(PR_FALSE)
   , mResponseHeadersModified(PR_FALSE)
   , mAllowPipelining(PR_TRUE)
   , mForceAllowThirdPartyCookie(PR_FALSE)
   , mUploadStreamHasHeaders(PR_FALSE)
+  , mInheritApplicationCache(PR_TRUE)
+  , mChooseApplicationCache(PR_FALSE)
+  , mLoadedFromApplicationCache(PR_FALSE)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // grab a reference to the handler to ensure that it doesn't go away.
   NS_ADDREF(gHttpHandler);
 }
 
 HttpBaseChannel::~HttpBaseChannel()
@@ -975,13 +984,142 @@ HttpBaseChannel::AddCookiesToRequest()
   }
 
   // overwrite any existing cookie headers.  be sure to clear any
   // existing cookies if we have no cookies to set or if the cookie
   // service is unavailable.
   SetRequestHeader(nsDependentCString(nsHttp::Cookie), cookie, PR_FALSE);
 }
 
+static PLDHashOperator
+CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure)
+{
+  nsIWritablePropertyBag* bag = static_cast<nsIWritablePropertyBag*>
+                                           (aClosure);
+  bag->SetProperty(aKey, aData);
+  return PL_DHASH_NEXT;
+}
+
+nsresult
+HttpBaseChannel::SetupReplacementChannel(nsIURI       *newURI, 
+                                         nsIChannel   *newChannel,
+                                         PRBool        preserveMethod)
+{
+  LOG(("HttpBaseChannel::SetupReplacementChannel "
+     "[this=%p newChannel=%p preserveMethod=%d]",
+     this, newChannel, preserveMethod));
+  PRUint32 newLoadFlags = mLoadFlags | LOAD_REPLACE;
+  // if the original channel was using SSL and this channel is not using
+  // SSL, then no need to inhibit persistent caching.  however, if the
+  // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING
+  // set, then allow the flag to apply to the redirected channel as well.
+  // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels,
+  // we only need to check if the original channel was using SSL.
+  if (mConnectionInfo->UsingSSL())
+    newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING;
+
+  // Do not pass along LOAD_CHECK_OFFLINE_CACHE
+  newLoadFlags &= ~nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE;
+
+  newChannel->SetLoadGroup(mLoadGroup); 
+  newChannel->SetNotificationCallbacks(mCallbacks);
+  newChannel->SetLoadFlags(newLoadFlags);
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
+  if (!httpChannel)
+    return NS_OK; // no other options to set
+
+  if (preserveMethod) {
+    nsCOMPtr<nsIUploadChannel> uploadChannel =
+      do_QueryInterface(httpChannel);
+    nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
+      do_QueryInterface(httpChannel);
+    if (mUploadStream && (uploadChannel2 || uploadChannel)) {
+      // rewind upload stream
+      nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
+      if (seekable)
+        seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+
+      // replicate original call to SetUploadStream...
+      if (uploadChannel2) {
+        const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type);
+        if (!ctype)
+          ctype = "";
+        const char *clen  = mRequestHead.PeekHeader(nsHttp::Content_Length);
+        PRInt64 len = clen ? nsCRT::atoll(clen) : -1;
+        uploadChannel2->ExplicitSetUploadStream(
+            mUploadStream,
+            nsDependentCString(ctype),
+            len,
+            nsDependentCString(mRequestHead.Method()),
+            mUploadStreamHasHeaders);
+      } else {
+        if (mUploadStreamHasHeaders) {
+          uploadChannel->SetUploadStream(mUploadStream, EmptyCString(),
+                           -1);
+        } else {
+          const char *ctype =
+            mRequestHead.PeekHeader(nsHttp::Content_Type);
+          const char *clen =
+            mRequestHead.PeekHeader(nsHttp::Content_Length);
+          if (!ctype) {
+            ctype = "application/octet-stream";
+          }
+          if (clen) {
+            uploadChannel->SetUploadStream(mUploadStream,
+                             nsDependentCString(ctype),
+                             atoi(clen));
+          }
+        }
+      }
+    }
+    // since preserveMethod is true, we need to ensure that the appropriate 
+    // request method gets set on the channel, regardless of whether or not 
+    // we set the upload stream above. This means SetRequestMethod() will
+    // be called twice if ExplicitSetUploadStream() gets called above.
+
+    httpChannel->SetRequestMethod(nsDependentCString(mRequestHead.Method()));
+  }
+  // convey the referrer if one was used for this channel to the next one
+  if (mReferrer)
+    httpChannel->SetReferrer(mReferrer);
+  // convey the mAllowPipelining flag
+  httpChannel->SetAllowPipelining(mAllowPipelining);
+  // convey the new redirection limit
+  httpChannel->SetRedirectionLimit(mRedirectionLimit - 1);
+
+  nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
+  if (httpInternal) {
+    // convey the mForceAllowThirdPartyCookie flag
+    httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie);
+
+    // update the DocumentURI indicator since we are being redirected.
+    // if this was a top-level document channel, then the new channel
+    // should have its mDocumentURI point to newURI; otherwise, we
+    // just need to pass along our mDocumentURI to the new channel.
+    if (newURI && (mURI == mDocumentURI))
+      httpInternal->SetDocumentURI(newURI);
+    else
+      httpInternal->SetDocumentURI(mDocumentURI);
+  } 
+  
+  // transfer application cache information
+  nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
+    do_QueryInterface(newChannel);
+  if (appCacheChannel) {
+    appCacheChannel->SetApplicationCache(mApplicationCache);
+    appCacheChannel->SetInheritApplicationCache(mInheritApplicationCache);
+    // We purposely avoid transfering mChooseApplicationCache.
+  }
+
+  // transfer any properties
+  nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel));
+  if (bag)
+    mPropertyHash.EnumerateRead(CopyProperties, bag.get());
+
+  return NS_OK;
+}
+
 //------------------------------------------------------------------------------
 
 }  // namespace net
 }  // namespace mozilla
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -50,16 +50,17 @@
 #include "nsHttpConnectionInfo.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIUploadChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsIProgressEventSink.h"
 #include "nsIURI.h"
 #include "nsISupportsPriority.h"
+#include "nsIApplicationCache.h"
 
 #define DIE_WITH_ASYNC_OPEN_MSG()                                              \
   do {                                                                         \
     fprintf(stderr,                                                            \
             "*&*&*&*&*&*&*&**&*&&*& FATAL ERROR: '%s' "                        \
             "called after AsyncOpen: %s +%d",                                  \
             __FUNCTION__, __FILE__, __LINE__);                                 \
     NS_ABORT();                                                                \
@@ -164,16 +165,19 @@ public:
   NS_IMETHOD GetCanceled(PRBool *aCanceled);
 
   // nsISupportsPriority
   NS_IMETHOD GetPriority(PRInt32 *value);
   NS_IMETHOD AdjustPriority(PRInt32 delta);
 
 protected:
   void AddCookiesToRequest();
+  virtual nsresult SetupReplacementChannel(nsIURI *,
+                                           nsIChannel *,
+                                           PRBool preserveMethod);
 
   // Helper function to simplify getting notification callbacks.
   template <class T>
   void GetCallback(nsCOMPtr<T> &aResult)
   {
     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                   NS_GET_TEMPLATE_IID(T),
                                   getter_AddRefs(aResult));
@@ -184,16 +188,17 @@ protected:
   nsCOMPtr<nsIURI>                  mDocumentURI;
   nsCOMPtr<nsIStreamListener>       mListener;
   nsCOMPtr<nsISupports>             mListenerContext;
   nsCOMPtr<nsILoadGroup>            mLoadGroup;
   nsCOMPtr<nsISupports>             mOwner;
   nsCOMPtr<nsIInterfaceRequestor>   mCallbacks;
   nsCOMPtr<nsIProgressEventSink>    mProgressSink;
   nsCOMPtr<nsIURI>                  mReferrer;
+  nsCOMPtr<nsIApplicationCache>     mApplicationCache;
 
   nsHttpRequestHead                 mRequestHead;
   nsCOMPtr<nsIInputStream>          mUploadStream;
   nsAutoPtr<nsHttpResponseHead>     mResponseHead;
   nsRefPtr<nsHttpConnectionInfo>    mConnectionInfo;
 
   nsCString                         mSpec; // ASCII encoded URL spec
   nsCString                         mContentTypeHint;
@@ -201,22 +206,25 @@ protected:
   nsCString                         mUserSetCookieHeader;
 
   nsresult                          mStatus;
   PRUint32                          mLoadFlags;
   PRInt16                           mPriority;
   PRUint8                           mCaps;
   PRUint8                           mRedirectionLimit;
 
-  PRUint8                           mCanceled                   : 1;
-  PRUint8                           mIsPending                  : 1;
-  PRUint8                           mWasOpened                  : 1;
-  PRUint8                           mResponseHeadersModified    : 1;
-  PRUint8                           mAllowPipelining            : 1;
-  PRUint8                           mForceAllowThirdPartyCookie : 1;
-  PRUint8                           mUploadStreamHasHeaders     : 1;
+  PRUint32                          mCanceled                   : 1;
+  PRUint32                          mIsPending                  : 1;
+  PRUint32                          mWasOpened                  : 1;
+  PRUint32                          mResponseHeadersModified    : 1;
+  PRUint32                          mAllowPipelining            : 1;
+  PRUint32                          mForceAllowThirdPartyCookie : 1;
+  PRUint32                          mUploadStreamHasHeaders     : 1;
+  PRUint32                          mInheritApplicationCache    : 1;
+  PRUint32                          mChooseApplicationCache     : 1;
+  PRUint32                          mLoadedFromApplicationCache : 1;
 };
 
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_HttpBaseChannel_h
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -19,16 +19,17 @@
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
  *   Daniel Witte <dwitte@mozilla.com>
+ *   Honza Bambas <honzab@firemni.cz>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -70,17 +71,21 @@ public:
   ~AutoEventEnqueuer() 
   { 
     mChannel->FlushEventQueue(); 
   }
 private:
     HttpChannelChild *mChannel;
 };
 
-// C++ file contents
+
+//-----------------------------------------------------------------------------
+// HttpChannelChild
+//-----------------------------------------------------------------------------
+
 HttpChannelChild::HttpChannelChild()
   : mIsFromCache(PR_FALSE)
   , mCacheEntryAvailable(PR_FALSE)
   , mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME)
   , mState(HCC_NEW)
   , mIPCOpen(false)
   , mQueuePhase(PHASE_UNQUEUED)
 {
@@ -108,16 +113,17 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
   NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
   NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel)
   NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
   NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
   NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::PHttpChannelChild
 //-----------------------------------------------------------------------------
 
 void
 HttpChannelChild::AddIPDLReference()
@@ -468,16 +474,170 @@ HttpChannelChild::OnStatus(const nsresul
   // block socket status event after Cancel or OnStopRequest has been called.
   if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && 
       !(mLoadFlags & LOAD_BACKGROUND)) 
   {
     mProgressSink->OnStatus(this, nsnull, status, statusArg.get());
   }
 }
 
+class Redirect1Event : public ChildChannelEvent
+{
+ public:
+  Redirect1Event(HttpChannelChild* child,
+                 PHttpChannelChild* newChannel,
+                 const IPC::URI& newURI,
+                 const PRUint32& redirectFlags,
+                 const nsHttpResponseHead& responseHead)
+  : mChild(child)
+  , mNewChannel(newChannel)
+  , mNewURI(newURI)
+  , mRedirectFlags(redirectFlags)
+  , mResponseHead(responseHead) {}
+
+  void Run() 
+  { 
+    mChild->Redirect1Begin(mNewChannel, mNewURI, mRedirectFlags, 
+                           mResponseHead); 
+  }
+ private:
+  HttpChannelChild*   mChild;
+  PHttpChannelChild*  mNewChannel;
+  IPC::URI            mNewURI;
+  PRUint32            mRedirectFlags;
+  nsHttpResponseHead  mResponseHead;
+};
+
+bool
+HttpChannelChild::RecvRedirect1Begin(PHttpChannelChild* newChannel,
+                                     const IPC::URI& newURI,
+                                     const PRUint32& redirectFlags,
+                                     const nsHttpResponseHead& responseHead)
+{
+  if (ShouldEnqueue()) {
+    EnqueueEvent(new Redirect1Event(this, newChannel, newURI, redirectFlags, 
+                                    responseHead)); 
+  } else {
+    Redirect1Begin(newChannel, newURI, redirectFlags, responseHead);
+  }
+  return true;
+}
+
+void
+HttpChannelChild::Redirect1Begin(PHttpChannelChild* newChannel,
+                                 const IPC::URI& newURI,
+                                 const PRUint32& redirectFlags,
+                                 const nsHttpResponseHead& responseHead)
+{
+  HttpChannelChild* 
+    newHttpChannelChild = static_cast<HttpChannelChild*>(newChannel);
+  nsCOMPtr<nsIURI> uri(newURI);
+
+  nsresult rv = 
+    newHttpChannelChild->HttpBaseChannel::Init(uri, mCaps,
+                                               mConnectionInfo->ProxyInfo());
+  if (NS_FAILED(rv))
+    return; // TODO Bug 536317
+
+  // We won't get OnStartRequest, set cookies here.
+  mResponseHead = new nsHttpResponseHead(responseHead);
+  SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
+
+  PRBool preserveMethod = (mResponseHead->Status() == 307);
+  rv = SetupReplacementChannel(uri, newHttpChannelChild, preserveMethod);
+  if (NS_FAILED(rv))
+    return; // TODO Bug 536317
+
+  mRedirectChannelChild = newHttpChannelChild;
+
+  nsresult result = gHttpHandler->AsyncOnChannelRedirect(this, 
+                                                         newHttpChannelChild, 
+                                                         redirectFlags);
+  if (NS_FAILED(result))
+    OnRedirectVerifyCallback(result);
+}
+
+class Redirect3Event : public ChildChannelEvent
+{
+ public:
+  Redirect3Event(HttpChannelChild* child) : mChild(child) {}
+  void Run() { mChild->Redirect3Complete(); }
+ private:
+  HttpChannelChild* mChild;
+};
+
+bool
+HttpChannelChild::RecvRedirect3Complete()
+{
+  if (ShouldEnqueue()) {
+    EnqueueEvent(new Redirect3Event(this));
+  } else {
+    Redirect3Complete();
+  }
+  return true;
+}
+
+void
+HttpChannelChild::Redirect3Complete()
+{
+  nsresult rv;
+
+  // Redirecting to new channel: shut this down and init new channel
+  if (mLoadGroup)
+    mLoadGroup->RemoveRequest(this, nsnull, NS_BINDING_ABORTED);
+
+  // Chrome channel has been AsyncOpen'd.  Reflect this in child.
+  rv = mRedirectChannelChild->CompleteRedirectSetup(mListener, 
+                                                    mListenerContext);
+  if (NS_FAILED(rv))
+    ; // TODO Cancel: Bug 536317 
+}
+
+nsresult
+HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, 
+                                        nsISupports *aContext)
+{
+  LOG(("HttpChannelChild::FinishRedirectSetup [this=%x]\n", this));
+
+  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+  // notify "http-on-modify-request" observers
+  gHttpHandler->OnModifyRequest(this);
+
+  mIsPending = PR_TRUE;
+  mWasOpened = PR_TRUE;
+  mListener = listener;
+  mListenerContext = aContext;
+
+  // add ourselves to the load group. 
+  if (mLoadGroup)
+    mLoadGroup->AddRequest(this, nsnull);
+
+  // TODO: may have been canceled by on-modify-request observers: bug 536317
+
+  mState = HCC_OPENED;
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// HttpChannelChild::nsIAsyncVerifyRedirectCallback
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+HttpChannelChild::OnRedirectVerifyCallback(nsresult result)
+{
+  // Cookies may have been changed by redirect observers
+  mRedirectChannelChild->AddCookiesToRequest();
+  // Must not be called until after redirect observers called.
+  mRedirectChannelChild->SetOriginalURI(mRedirectOriginalURI);
+
+  return SendRedirect2Result(result, mRedirectChannelChild->mRequestHeaders);
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::Cancel(nsresult status)
 {
   // FIXME: bug 536317
@@ -595,26 +755,26 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
 
   mozilla::dom::TabChild* tabChild = nsnull;
   nsCOMPtr<nsITabChild> iTabChild;
   GetCallback(iTabChild);
   if (iTabChild) {
     tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
   }
 
-  // The socket transport layer in the chrome process now has a logical ref to
-  // us, until either OnStopRequest or OnRedirect is called.
+  // The socket transport in the chrome process now holds a logical ref to us
+  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
   AddIPDLReference();
 
   gNeckoChild->SendPHttpChannelConstructor(this, tabChild);
 
-  SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI), IPC::URI(mDocumentURI),
-                IPC::URI(mReferrer), mLoadFlags, mRequestHeaders, 
-                mRequestHead.Method(), uploadStreamData, 
-                uploadStreamInfo, mPriority, mRedirectionLimit, 
+  SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI),
+                IPC::URI(mDocumentURI), IPC::URI(mReferrer), mLoadFlags,
+                mRequestHeaders, mRequestHead.Method(), uploadStreamData,
+                uploadStreamInfo, mPriority, mRedirectionLimit,
                 mAllowPipelining, mForceAllowThirdPartyCookie);
 
   mState = HCC_OPENED;
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIHttpChannel
@@ -779,17 +939,18 @@ HttpChannelChild::SetNewListener(nsIStre
 NS_IMETHODIMP
 HttpChannelChild::GetApplicationCache(nsIApplicationCache **aApplicationCache)
 {
   DROP_DEAD();
 }
 NS_IMETHODIMP
 HttpChannelChild::SetApplicationCache(nsIApplicationCache *aApplicationCache)
 {
-  DROP_DEAD();
+  // FIXME: redirects call. so stub OK for now. Fix in bug 536295.  
+  return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIApplicationCacheChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::GetLoadedFromApplicationCache(PRBool *retval)
@@ -820,10 +981,11 @@ NS_IMETHODIMP
 HttpChannelChild::SetChooseApplicationCache(PRBool aChooseApplicationCache)
 {
   // FIXME: Browser calls this early, so stub OK for now. Fix in bug 536295.  
   return NS_OK;
 }
 
 
 //------------------------------------------------------------------------------
+
 }} // mozilla::net
 
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -19,16 +19,17 @@
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
  *   Daniel Witte <dwitte@mozilla.com>
+ *   Honza Bambas <honzab@firemni.cz>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -53,50 +54,52 @@
 #include "nsICacheInfoChannel.h"
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIEncodedChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsIResumableChannel.h"
 #include "nsIProxiedChannel.h"
 #include "nsITraceableChannel.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 namespace mozilla {
 namespace net {
 
 class ChildChannelEvent;
 
 // TODO: replace with IPDL states: bug 536319
 enum HttpChannelChildState {
   HCC_NEW,
   HCC_OPENED,
   HCC_ONSTART,
   HCC_ONDATA,
   HCC_ONSTOP
 };
 
-// Header file contents
 class HttpChannelChild : public PHttpChannelChild
                        , public HttpBaseChannel
                        , public nsICacheInfoChannel
                        , public nsIEncodedChannel
                        , public nsIResumableChannel
                        , public nsIProxiedChannel
                        , public nsITraceableChannel
                        , public nsIApplicationCacheChannel
+                       , public nsIAsyncVerifyRedirectCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSICACHEINFOCHANNEL
   NS_DECL_NSIENCODEDCHANNEL
   NS_DECL_NSIRESUMABLECHANNEL
   NS_DECL_NSIPROXIEDCHANNEL
   NS_DECL_NSITRACEABLECHANNEL
   NS_DECL_NSIAPPLICATIONCACHECONTAINER
   NS_DECL_NSIAPPLICATIONCACHECHANNEL
+  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
 
   HttpChannelChild();
   virtual ~HttpChannelChild();
 
   // Methods HttpBaseChannel didn't implement for us or that we override.
   //
   // nsIRequest
   NS_IMETHOD Cancel(nsresult status);
@@ -109,16 +112,20 @@ public:
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader, 
                               const nsACString& aValue, 
                               PRBool aMerge);
   // nsIHttpChannelInternal
   NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
   // nsISupportsPriority
   NS_IMETHOD SetPriority(PRInt32 value);
 
+  // Final setup when redirect has proceeded successfully in chrome
+  nsresult CompleteRedirectSetup(nsIStreamListener *listener, 
+                                 nsISupports *aContext);
+
   // IPDL holds a reference while the PHttpChannel protocol is live (starting at
   // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of
   // which call NeckoChild::DeallocPHttpChannel()).
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
 protected:
   bool RecvOnStartRequest(const nsHttpResponseHead& responseHead,
@@ -128,19 +135,26 @@ protected:
                           const PRUint32& cacheExpirationTime,
                           const nsCString& cachedCharset);
   bool RecvOnDataAvailable(const nsCString& data, 
                            const PRUint32& offset,
                            const PRUint32& count);
   bool RecvOnStopRequest(const nsresult& statusCode);
   bool RecvOnProgress(const PRUint64& progress, const PRUint64& progressMax);
   bool RecvOnStatus(const nsresult& status, const nsString& statusArg);
+  bool RecvRedirect1Begin(PHttpChannelChild* newChannel,
+                          const URI& newURI,
+                          const PRUint32& redirectFlags,
+                          const nsHttpResponseHead& responseHead);
+  bool RecvRedirect3Complete();
 
 private:
   RequestHeaderTuples mRequestHeaders;
+  nsRefPtr<HttpChannelChild> mRedirectChannelChild;
+  nsCOMPtr<nsIURI> mRedirectOriginalURI;
 
   PRPackedBool mIsFromCache;
   PRPackedBool mCacheEntryAvailable;
   PRUint32     mCacheExpirationTime;
   nsCString    mCachedCharset;
 
   // FIXME: replace with IPDL states (bug 536319) 
   enum HttpChannelChildState mState;
@@ -170,23 +184,29 @@ private:
                           const PRUint32& cacheExpirationTime,
                           const nsCString& cachedCharset);
   void OnDataAvailable(const nsCString& data, 
                        const PRUint32& offset,
                        const PRUint32& count);
   void OnStopRequest(const nsresult& statusCode);
   void OnProgress(const PRUint64& progress, const PRUint64& progressMax);
   void OnStatus(const nsresult& status, const nsString& statusArg);
+  void Redirect1Begin(PHttpChannelChild* newChannel, const URI& newURI,
+                      const PRUint32& redirectFlags,
+                      const nsHttpResponseHead& responseHead);
+  void Redirect3Complete();
 
   friend class AutoEventEnqueuer;
   friend class StartRequestEvent;
   friend class StopRequestEvent;
   friend class DataAvailableEvent;
   friend class ProgressEvent;
   friend class StatusEvent;
+  friend class Redirect1Event;
+  friend class Redirect3Event;
 };
 
 //-----------------------------------------------------------------------------
 // inline functions
 //-----------------------------------------------------------------------------
 
 inline void
 HttpChannelChild::BeginEventQueueing()
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -18,16 +18,17 @@
  *
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
+ *   Honza Bambas <honzab@firemni.cz>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -35,29 +36,30 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/net/NeckoParent.h"
+#include "HttpChannelParentListener.h"
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsNetUtil.h"
 #include "nsISupportsPriority.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIBadCertListener2.h"
 #include "nsICacheEntryDescriptor.h"
 
 namespace mozilla {
 namespace net {
 
-// C++ file contents
 HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding)
 : mIPCClosed(false)
 {
   // Ensure gHttpHandler is initialized: we need the atom table up and running.
   nsIHttpProtocolHandler* handler;
   CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &handler);
   NS_ASSERTION(handler, "no http handler");
 
@@ -77,21 +79,18 @@ HttpChannelParent::ActorDestroy(ActorDes
   // yet, but we must not send any more msgs to child.
   mIPCClosed = true;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_ISUPPORTS4(HttpChannelParent, 
-                   nsIRequestObserver, 
-                   nsIStreamListener,
-                   nsIInterfaceRequestor,
-                   nsIProgressEventSink);
+NS_IMPL_ISUPPORTS1(HttpChannelParent,
+                   nsIProgressEventSink)
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::PHttpChannelParent
 //-----------------------------------------------------------------------------
 
 bool 
 HttpChannelParent::RecvAsyncOpen(const IPC::URI&            aURI,
                                  const IPC::URI&            aOriginalURI,
@@ -140,17 +139,19 @@ HttpChannelParent::RecvAsyncOpen(const I
     httpChan->SetLoadFlags(loadFlags);
 
   for (PRUint32 i = 0; i < requestHeaders.Length(); i++) {
     httpChan->SetRequestHeader(requestHeaders[i].mHeader,
                                requestHeaders[i].mValue,
                                requestHeaders[i].mMerge);
   }
 
-  httpChan->SetNotificationCallbacks(this);
+  mChannelListener = new HttpChannelParentListener(this);
+
+  httpChan->SetNotificationCallbacks(mChannelListener);
 
   httpChan->SetRequestMethod(nsDependentCString(requestMethod.get()));
 
   if (uploadStreamInfo != eUploadStream_null) {
     nsCOMPtr<nsIInputStream> stream;
     rv = NS_NewPostDataStream(getter_AddRefs(stream), false, uploadStreamData, 0);
     if (!NS_SUCCEEDED(rv)) {
       return false;   // TODO: cancel request (bug 536317), return true
@@ -162,17 +163,17 @@ HttpChannelParent::RecvAsyncOpen(const I
   }
 
   if (priority != nsISupportsPriority::PRIORITY_NORMAL)
     httpChan->SetPriority(priority);
   httpChan->SetRedirectionLimit(redirectionLimit);
   httpChan->SetAllowPipelining(allowPipelining);
   httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
 
-  rv = httpChan->AsyncOpen(this, nsnull);
+  rv = httpChan->AsyncOpen(mChannelListener, nsnull);
   if (NS_FAILED(rv))
     return false;       // TODO: cancel request (bug 536317), return true
 
   return true;
 }
 
 bool 
 HttpChannelParent::RecvSetPriority(const PRUint16& priority)
@@ -186,25 +187,38 @@ bool
 HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
 {
   if (mCacheDescriptor)
     mCacheDescriptor->SetMetaDataElement("charset",
                                          PromiseFlatCString(charset).get());
   return true;
 }
 
+bool
+HttpChannelParent::RecvRedirect2Result(const nsresult& result, 
+                                       const RequestHeaderTuples& changedHeaders)
+{
+  if (mChannelListener)
+    mChannelListener->OnContentRedirectResultReceived(result, changedHeaders);
+  return true;
+}
+
 //-----------------------------------------------------------------------------
-// HttpChannelParent::nsIRequestObserver
+// nsIRequestObserver and nsIStreamListener methods equivalents
 //-----------------------------------------------------------------------------
 
-NS_IMETHODIMP
+nsresult
 HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
   LOG(("HttpChannelParent::OnStartRequest [this=%x]\n", this));
 
+  // We need this member only to call OnContentRedirectResultReceived on it, 
+  // that will for sure not happen when we get here.  Throw it away ASAP.
+  mChannelListener = nsnull;
+
   nsHttpChannel *chan = static_cast<nsHttpChannel *>(aRequest);
   nsHttpResponseHead *responseHead = chan->GetResponseHead();
 
   PRBool isFromCache = false;
   chan->IsFromCache(&isFromCache);
   PRUint32 expirationTime;
   chan->GetCacheTokenExpirationTime(&expirationTime);
   nsCString cachedCharset;
@@ -219,34 +233,30 @@ HttpChannelParent::OnStartRequest(nsIReq
                           !!responseHead, isFromCache,
                           mCacheDescriptor ? PR_TRUE : PR_FALSE,
                           expirationTime, cachedCharset)) {
     return NS_ERROR_UNEXPECTED; 
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 HttpChannelParent::OnStopRequest(nsIRequest *aRequest, 
                                  nsISupports *aContext, 
                                  nsresult aStatusCode)
 {
   LOG(("HttpChannelParent::OnStopRequest: [this=%x status=%ul]\n", 
        this, aStatusCode));
 
   if (mIPCClosed || !SendOnStopRequest(aStatusCode))
     return NS_ERROR_UNEXPECTED; 
   return NS_OK;
 }
 
-//-----------------------------------------------------------------------------
-// HttpChannelParent::nsIStreamListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
+nsresult
 HttpChannelParent::OnDataAvailable(nsIRequest *aRequest, 
                                    nsISupports *aContext, 
                                    nsIInputStream *aInputStream, 
                                    PRUint32 aOffset, 
                                    PRUint32 aCount)
 {
   LOG(("HttpChannelParent::OnDataAvailable [this=%x]\n", this));
  
@@ -263,72 +273,19 @@ HttpChannelParent::OnDataAvailable(nsIRe
   }
 
   if (mIPCClosed || !SendOnDataAvailable(data, aOffset, bytesRead))
     return NS_ERROR_UNEXPECTED; 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// HttpChannelParent::nsIInterfaceRequestor
+// HttpChannelParent::nsIProgressEventSink
 //-----------------------------------------------------------------------------
 
-NS_IMETHODIMP 
-HttpChannelParent::GetInterface(const nsIID& aIID, void **result)
-{
-  if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider))) {
-    if (!mTabParent)
-      return NS_NOINTERFACE;
-    return mTabParent->QueryInterface(aIID, result);
-  }
-
-  // TODO: 575494: once we're confident we're handling all needed interfaces,
-  // remove all code below and simply "return QueryInterface(aIID, result)"
-  if (// Known interface calls:
-
-      // FIXME: HTTP Authorization (bug 537782):
-      // nsHttpChannel first tries to get this as an nsIAuthPromptProvider; if that
-      // fails, it tries as an nsIAuthPrompt2, and if that fails, an nsIAuthPrompt.
-      // See nsHttpChannel::GetAuthPrompt().  So if we can return any one of these,
-      // HTTP auth should be all set.  The other two if checks can be eventually
-      // deleted.
-      aIID.Equals(NS_GET_IID(nsIAuthPrompt2)) ||
-      aIID.Equals(NS_GET_IID(nsIAuthPrompt))  ||
-      // FIXME: redirects (bug 536294):
-      // The likely solution here is for this class to implement nsIChannelEventSink
-      // and nsIHttpEventSink (and forward calls to any real sinks in the child), in
-      // which case QueryInterface() will do the work here and these if statements
-      // can be eventually discarded.
-      aIID.Equals(NS_GET_IID(nsIChannelEventSink)) || 
-      aIID.Equals(NS_GET_IID(nsIHttpEventSink))  ||
-      // FIXME: application cache (bug 536295):
-      aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer)) ||
-      aIID.Equals(NS_GET_IID(nsIProgressEventSink)) ||
-      // FIXME:  bug 561830: when fixed, we shouldn't be asked for this interface
-      aIID.Equals(NS_GET_IID(nsIDocShellTreeItem)) ||
-      // Let this return NS_ERROR_NO_INTERFACE: it's OK to not provide it.
-      aIID.Equals(NS_GET_IID(nsIBadCertListener2))) 
-  {
-    return QueryInterface(aIID, result);
-  } else {
-    nsPrintfCString msg(2000, 
-       "HttpChannelParent::GetInterface: interface UUID=%s not yet supported! "
-       "Use 'grep -ri UUID <mozilla_src>' to find the name of the interface, "
-       "check http://tinyurl.com/255ojvu to see if a bug has already been "
-       "filed, and if not, add one and make it block bug 516730. Thanks!",
-       aIID.ToString());
-    NECKO_MAYBE_ABORT(msg);
-    return NS_NOINTERFACE;
-  }
-}
-
-//-----------------------------------------------------------------------------
-// HttpChannelParent::nsIProgressEventSink
-//-----------------------------------------------------------------------------
- 
 NS_IMETHODIMP
 HttpChannelParent::OnProgress(nsIRequest *aRequest, 
                               nsISupports *aContext, 
                               PRUint64 aProgress, 
                               PRUint64 aProgressMax)
 {
   if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax))
     return NS_ERROR_UNEXPECTED;
@@ -341,11 +298,10 @@ HttpChannelParent::OnStatus(nsIRequest *
                             nsresult aStatus, 
                             const PRUnichar *aStatusArg)
 {
   if (mIPCClosed || !SendOnStatus(aStatus, nsString(aStatusArg)))
     return NS_ERROR_UNEXPECTED;
   return NS_OK;
 }
 
-
 }} // mozilla::net
 
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -18,16 +18,17 @@
  *
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
+ *   Honza Bambas <honzab@firemni.cz>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -40,41 +41,47 @@
 
 #ifndef mozilla_net_HttpChannelParent_h
 #define mozilla_net_HttpChannelParent_h
 
 #include "nsHttp.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/net/PHttpChannelParent.h"
 #include "mozilla/net/NeckoCommon.h"
-#include "nsIStreamListener.h"
-#include "nsIInterfaceRequestor.h"
 #include "nsIProgressEventSink.h"
 #include "nsITabParent.h"
 
 using namespace mozilla::dom;
 
 class nsICacheEntryDescriptor;
 
 namespace mozilla {
 namespace net {
 
-// Header file contents
+class HttpChannelParentListener;
+
 class HttpChannelParent : public PHttpChannelParent
-                        , public nsIStreamListener
-                        , public nsIInterfaceRequestor
                         , public nsIProgressEventSink
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIREQUESTOBSERVER
-  NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIPROGRESSEVENTSINK
 
+  // Make these non-virtual for a little performance benefit
+  nsresult OnStartRequest(nsIRequest *aRequest, 
+                          nsISupports *aContext);
+  nsresult OnStopRequest(nsIRequest *aRequest, 
+                         nsISupports *aContext, 
+                         nsresult aStatusCode);
+  nsresult OnDataAvailable(nsIRequest *aRequest, 
+                           nsISupports *aContext, 
+                           nsIInputStream *aInputStream, 
+                           PRUint32 aOffset, 
+                           PRUint32 aCount);
+  
   HttpChannelParent(PBrowserParent* iframeEmbedding);
   virtual ~HttpChannelParent();
 
 protected:
   virtual bool RecvAsyncOpen(const IPC::URI&            uri,
                              const IPC::URI&            originalUri,
                              const IPC::URI&            docUri,
                              const IPC::URI&            referrerUri,
@@ -85,22 +92,28 @@ protected:
                              const PRInt32&             uploadStreamInfo,
                              const PRUint16&            priority,
                              const PRUint8&             redirectionLimit,
                              const PRBool&              allowPipelining,
                              const PRBool&              forceAllowThirdPartyCookie);
 
   virtual bool RecvSetPriority(const PRUint16& priority);
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
+  virtual bool RecvRedirect2Result(const nsresult& result,
+                                   const RequestHeaderTuples& changedHeaders);
 
   virtual void ActorDestroy(ActorDestroyReason why);
 
-private:
+protected:
+  friend class mozilla::net::HttpChannelParentListener;
   nsCOMPtr<nsITabParent> mTabParent;
+
+private:
   nsCOMPtr<nsIChannel> mChannel;
+  nsRefPtr<HttpChannelParentListener> mChannelListener;
   nsCOMPtr<nsICacheEntryDescriptor> mCacheDescriptor;
   bool mIPCClosed;                // PHttpChannel actor has been Closed()
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_HttpChannelParent_h
copy from netwerk/protocol/http/HttpChannelParent.cpp
copy to netwerk/protocol/http/HttpChannelParentListener.cpp
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParentListener.cpp
@@ -18,271 +18,137 @@
  *
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
+ *   Honza Bambas <honzab@firemni.cz> 
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "HttpChannelParentListener.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/net/NeckoParent.h"
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsNetUtil.h"
 #include "nsISupportsPriority.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIBadCertListener2.h"
 #include "nsICacheEntryDescriptor.h"
+#include "nsSerializationHelper.h"
+#include "nsISerializable.h"
+#include "nsIAssociatedContentSecurity.h"
+#include "nsISecureBrowserUI.h"
 
 namespace mozilla {
 namespace net {
 
-// C++ file contents
-HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding)
-: mIPCClosed(false)
+HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel)
+  : mActiveChannel(aInitialChannel)
 {
-  // Ensure gHttpHandler is initialized: we need the atom table up and running.
-  nsIHttpProtocolHandler* handler;
-  CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &handler);
-  NS_ASSERTION(handler, "no http handler");
-
-  mTabParent = do_QueryInterface(static_cast<nsITabParent*>(
-      static_cast<TabParent*>(iframeEmbedding)));
 }
 
-HttpChannelParent::~HttpChannelParent()
+HttpChannelParentListener::~HttpChannelParentListener()
 {
-  gHttpHandler->Release();
-}
-
-void
-HttpChannelParent::ActorDestroy(ActorDestroyReason why)
-{
-  // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
-  // yet, but we must not send any more msgs to child.
-  mIPCClosed = true;
 }
 
 //-----------------------------------------------------------------------------
-// HttpChannelParent::nsISupports
+// HttpChannelParentListener::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_ISUPPORTS4(HttpChannelParent, 
-                   nsIRequestObserver, 
+NS_IMPL_ISUPPORTS5(HttpChannelParentListener, 
+                   nsIInterfaceRequestor,
                    nsIStreamListener,
-                   nsIInterfaceRequestor,
-                   nsIProgressEventSink);
+                   nsIRequestObserver,
+                   nsIChannelEventSink,
+                   nsIRedirectResultListener)
 
 //-----------------------------------------------------------------------------
-// HttpChannelParent::PHttpChannelParent
+// HttpChannelParentListener::nsIRequestObserver
 //-----------------------------------------------------------------------------
 
-bool 
-HttpChannelParent::RecvAsyncOpen(const IPC::URI&            aURI,
-                                 const IPC::URI&            aOriginalURI,
-                                 const IPC::URI&            aDocURI,
-                                 const IPC::URI&            aReferrerURI,
-                                 const PRUint32&            loadFlags,
-                                 const RequestHeaderTuples& requestHeaders,
-                                 const nsHttpAtom&          requestMethod,
-                                 const nsCString&           uploadStreamData,
-                                 const PRInt32&             uploadStreamInfo,
-                                 const PRUint16&            priority,
-                                 const PRUint8&             redirectionLimit,
-                                 const PRBool&              allowPipelining,
-                                 const PRBool&              forceAllowThirdPartyCookie)
+NS_IMETHODIMP
+HttpChannelParentListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
-  nsCOMPtr<nsIURI> uri(aURI);
-  nsCOMPtr<nsIURI> originalUri(aOriginalURI);
-  nsCOMPtr<nsIURI> docUri(aDocURI);
-  nsCOMPtr<nsIURI> referrerUri(aReferrerURI);
-
-  nsCString uriSpec;
-  uri->GetSpec(uriSpec);
-  LOG(("HttpChannelParent RecvAsyncOpen [this=%x uri=%s]\n", 
-       this, uriSpec.get()));
-
-  nsresult rv;
-
-  nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
-  if (NS_FAILED(rv))
-    return false;       // TODO: cancel request (bug 536317), return true
-
-  rv = NS_NewChannel(getter_AddRefs(mChannel), uri, ios, nsnull, nsnull, loadFlags);
-  if (NS_FAILED(rv))
-    return false;       // TODO: cancel request (bug 536317), return true
-
-  nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
-  httpChan->SetRemoteChannel(true);
+  if (!mActiveChannel)
+    return NS_ERROR_UNEXPECTED;
 
-  if (originalUri)
-    httpChan->SetOriginalURI(originalUri);
-  if (docUri)
-    httpChan->SetDocumentURI(docUri);
-  if (referrerUri)
-    httpChan->SetReferrerInternal(referrerUri);
-  if (loadFlags != nsIRequest::LOAD_NORMAL)
-    httpChan->SetLoadFlags(loadFlags);
-
-  for (PRUint32 i = 0; i < requestHeaders.Length(); i++) {
-    httpChan->SetRequestHeader(requestHeaders[i].mHeader,
-                               requestHeaders[i].mValue,
-                               requestHeaders[i].mMerge);
-  }
-
-  httpChan->SetNotificationCallbacks(this);
-
-  httpChan->SetRequestMethod(nsDependentCString(requestMethod.get()));
-
-  if (uploadStreamInfo != eUploadStream_null) {
-    nsCOMPtr<nsIInputStream> stream;
-    rv = NS_NewPostDataStream(getter_AddRefs(stream), false, uploadStreamData, 0);
-    if (!NS_SUCCEEDED(rv)) {
-      return false;   // TODO: cancel request (bug 536317), return true
-    }
-    httpChan->InternalSetUploadStream(stream);
-    // We're casting uploadStreamInfo into PRBool here on purpose because
-    // we know possible values are either 0 or 1. See uploadStreamInfoType.
-    httpChan->SetUploadStreamHasHeaders((PRBool) uploadStreamInfo);
-  }
-
-  if (priority != nsISupportsPriority::PRIORITY_NORMAL)
-    httpChan->SetPriority(priority);
-  httpChan->SetRedirectionLimit(redirectionLimit);
-  httpChan->SetAllowPipelining(allowPipelining);
-  httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
-
-  rv = httpChan->AsyncOpen(this, nsnull);
-  if (NS_FAILED(rv))
-    return false;       // TODO: cancel request (bug 536317), return true
-
-  return true;
+  LOG(("HttpChannelParentListener::OnStartRequest [this=%x]\n", this));
+  return mActiveChannel->OnStartRequest(aRequest, aContext);
 }
 
-bool 
-HttpChannelParent::RecvSetPriority(const PRUint16& priority)
+NS_IMETHODIMP
+HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest, 
+                                          nsISupports *aContext, 
+                                          nsresult aStatusCode)
 {
-  nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
-  httpChan->SetPriority(priority);
-  return true;
-}
+  if (!mActiveChannel)
+    return NS_ERROR_UNEXPECTED;
 
-bool
-HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
-{
-  if (mCacheDescriptor)
-    mCacheDescriptor->SetMetaDataElement("charset",
-                                         PromiseFlatCString(charset).get());
-  return true;
+  LOG(("HttpChannelParentListener::OnStopRequest: [this=%x status=%ul]\n", 
+       this, aStatusCode));
+  nsresult rv = mActiveChannel->OnStopRequest(aRequest, aContext, aStatusCode);
+
+  mActiveChannel = nsnull;
+  return rv;
 }
 
 //-----------------------------------------------------------------------------
-// HttpChannelParent::nsIRequestObserver
+// HttpChannelParentListener::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
+HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest, 
+                                            nsISupports *aContext, 
+                                            nsIInputStream *aInputStream, 
+                                            PRUint32 aOffset, 
+                                            PRUint32 aCount)
 {
-  LOG(("HttpChannelParent::OnStartRequest [this=%x]\n", this));
-
-  nsHttpChannel *chan = static_cast<nsHttpChannel *>(aRequest);
-  nsHttpResponseHead *responseHead = chan->GetResponseHead();
-
-  PRBool isFromCache = false;
-  chan->IsFromCache(&isFromCache);
-  PRUint32 expirationTime;
-  chan->GetCacheTokenExpirationTime(&expirationTime);
-  nsCString cachedCharset;
-  chan->GetCacheTokenCachedCharset(cachedCharset);
-
-  // Keep the cache entry for future use in RecvSetCacheTokenCachedCharset().
-  // It could be already released by nsHttpChannel at that time.
-  chan->GetCacheToken(getter_AddRefs(mCacheDescriptor));
+  if (!mActiveChannel)
+    return NS_ERROR_UNEXPECTED;
 
-  if (mIPCClosed || 
-      !SendOnStartRequest(responseHead ? *responseHead : nsHttpResponseHead(), 
-                          !!responseHead, isFromCache,
-                          mCacheDescriptor ? PR_TRUE : PR_FALSE,
-                          expirationTime, cachedCharset)) {
-    return NS_ERROR_UNEXPECTED; 
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HttpChannelParent::OnStopRequest(nsIRequest *aRequest, 
-                                 nsISupports *aContext, 
-                                 nsresult aStatusCode)
-{
-  LOG(("HttpChannelParent::OnStopRequest: [this=%x status=%ul]\n", 
-       this, aStatusCode));
-
-  if (mIPCClosed || !SendOnStopRequest(aStatusCode))
-    return NS_ERROR_UNEXPECTED; 
-  return NS_OK;
+  LOG(("HttpChannelParentListener::OnDataAvailable [this=%x]\n", this));
+  return mActiveChannel->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
 }
 
 //-----------------------------------------------------------------------------
-// HttpChannelParent::nsIStreamListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-HttpChannelParent::OnDataAvailable(nsIRequest *aRequest, 
-                                   nsISupports *aContext, 
-                                   nsIInputStream *aInputStream, 
-                                   PRUint32 aOffset, 
-                                   PRUint32 aCount)
-{
-  LOG(("HttpChannelParent::OnDataAvailable [this=%x]\n", this));
- 
-  nsresult rv;
-
-  nsCString data;
-  data.SetLength(aCount);
-  char * p = data.BeginWriting();
-  PRUint32 bytesRead;
-  rv = aInputStream->Read(p, aCount, &bytesRead);
-  data.EndWriting();
-  if (!NS_SUCCEEDED(rv) || bytesRead != aCount) {
-    return rv;              // TODO: cancel request locally (bug 536317)
-  }
-
-  if (mIPCClosed || !SendOnDataAvailable(data, aOffset, bytesRead))
-    return NS_ERROR_UNEXPECTED; 
-  return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// HttpChannelParent::nsIInterfaceRequestor
+// HttpChannelParentListener::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP 
-HttpChannelParent::GetInterface(const nsIID& aIID, void **result)
+HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result)
 {
   if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider))) {
-    if (!mTabParent)
+    if (!mActiveChannel || !mActiveChannel->mTabParent)
       return NS_NOINTERFACE;
-    return mTabParent->QueryInterface(aIID, result);
+    return mActiveChannel->mTabParent->QueryInterface(aIID, result);
+  }
+
+  if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
+    if (!mActiveChannel)
+      return NS_NOINTERFACE;
+    return mActiveChannel->QueryInterface(aIID, result);
   }
 
   // TODO: 575494: once we're confident we're handling all needed interfaces,
   // remove all code below and simply "return QueryInterface(aIID, result)"
   if (// Known interface calls:
 
       // FIXME: HTTP Authorization (bug 537782):
       // nsHttpChannel first tries to get this as an nsIAuthPromptProvider; if that
@@ -294,58 +160,130 @@ HttpChannelParent::GetInterface(const ns
       aIID.Equals(NS_GET_IID(nsIAuthPrompt))  ||
       // FIXME: redirects (bug 536294):
       // The likely solution here is for this class to implement nsIChannelEventSink
       // and nsIHttpEventSink (and forward calls to any real sinks in the child), in
       // which case QueryInterface() will do the work here and these if statements
       // can be eventually discarded.
       aIID.Equals(NS_GET_IID(nsIChannelEventSink)) || 
       aIID.Equals(NS_GET_IID(nsIHttpEventSink))  ||
+      aIID.Equals(NS_GET_IID(nsIRedirectResultListener))  ||
       // FIXME: application cache (bug 536295):
       aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer)) ||
-      aIID.Equals(NS_GET_IID(nsIProgressEventSink)) ||
       // FIXME:  bug 561830: when fixed, we shouldn't be asked for this interface
       aIID.Equals(NS_GET_IID(nsIDocShellTreeItem)) ||
       // Let this return NS_ERROR_NO_INTERFACE: it's OK to not provide it.
       aIID.Equals(NS_GET_IID(nsIBadCertListener2))) 
   {
     return QueryInterface(aIID, result);
   } else {
     nsPrintfCString msg(2000, 
-       "HttpChannelParent::GetInterface: interface UUID=%s not yet supported! "
+       "HttpChannelParentListener::GetInterface: interface UUID=%s not yet supported! "
        "Use 'grep -ri UUID <mozilla_src>' to find the name of the interface, "
        "check http://tinyurl.com/255ojvu to see if a bug has already been "
        "filed, and if not, add one and make it block bug 516730. Thanks!",
        aIID.ToString());
     NECKO_MAYBE_ABORT(msg);
     return NS_NOINTERFACE;
   }
 }
 
 //-----------------------------------------------------------------------------
-// HttpChannelParent::nsIProgressEventSink
+// HttpChannelParentListener::nsIChannelEventSink
 //-----------------------------------------------------------------------------
- 
+
 NS_IMETHODIMP
-HttpChannelParent::OnProgress(nsIRequest *aRequest, 
-                              nsISupports *aContext, 
-                              PRUint64 aProgress, 
-                              PRUint64 aProgressMax)
+HttpChannelParentListener::AsyncOnChannelRedirect(
+                                    nsIChannel *oldChannel,
+                                    nsIChannel *newChannel,
+                                    PRUint32 redirectFlags,
+                                    nsIAsyncVerifyRedirectCallback* callback)
 {
-  if (mIPCClosed || !SendOnProgress(aProgress, aProgressMax))
-    return NS_ERROR_UNEXPECTED;
+  if (mActiveChannel->mIPCClosed)
+    return NS_BINDING_ABORTED;
+
+  // Create new PHttpChannel
+  PBrowserParent* browser = mActiveChannel->mTabParent ?
+      static_cast<TabParent*>(mActiveChannel->mTabParent.get()) : nsnull;
+  mRedirectChannel = static_cast<HttpChannelParent *>
+      (mActiveChannel->Manager()->SendPHttpChannelConstructor(browser));
+
+  // Join it with the correct channel
+  mRedirectChannel->mChannel = newChannel;
+
+  // Let the new channel also keep the wrapper in case we get another redirect
+  // response, it wouldn't be able to send back the redirect result.
+  mRedirectChannel->mChannelListener = this;
+
+  // And finally, let the content process decide to redirect or not.
+  mRedirectCallback = callback;
+
+  nsCOMPtr<nsIURI> newURI;
+  newChannel->GetURI(getter_AddRefs(newURI));
+
+  nsHttpChannel *oldHttpChannel = static_cast<nsHttpChannel *>(oldChannel);
+  // TODO: check mActiveChannel->mIPCClosed and return val from Send function
+  mActiveChannel->SendRedirect1Begin(mRedirectChannel,
+                                     IPC::URI(newURI),
+                                     redirectFlags,
+                                     *oldHttpChannel->GetResponseHead());
+
+  // mActiveChannel gets the response in RecvRedirect2Result and forwards it
+  // to this wrapper through OnContentRedirectResultReceived
+
   return NS_OK;
 }
 
+void
+HttpChannelParentListener::OnContentRedirectResultReceived(
+                                const nsresult result,
+                                const RequestHeaderTuples& changedHeaders)
+{
+  nsHttpChannel* newHttpChannel = 
+      static_cast<nsHttpChannel*>(mRedirectChannel->mChannel.get());
+
+  if (NS_SUCCEEDED(result)) {
+    for (PRUint32 i = 0; i < changedHeaders.Length(); i++) {
+      newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
+                                       changedHeaders[i].mValue,
+                                       changedHeaders[i].mMerge);
+    }
+  }
+
+  mRedirectCallback->OnRedirectVerifyCallback(result);
+  mRedirectCallback = nsnull;
+}
+
+//-----------------------------------------------------------------------------
+// HttpChannelParentListener::nsIRedirectResultListener
+//-----------------------------------------------------------------------------
+
 NS_IMETHODIMP
-HttpChannelParent::OnStatus(nsIRequest *aRequest, 
-                            nsISupports *aContext, 
-                            nsresult aStatus, 
-                            const PRUnichar *aStatusArg)
+HttpChannelParentListener::OnRedirectResult(PRBool succeeded)
 {
-  if (mIPCClosed || !SendOnStatus(aStatus, nsString(aStatusArg)))
-    return NS_ERROR_UNEXPECTED;
+  if (!mRedirectChannel) {
+    // Redirect might get canceled before we got AsyncOnChannelRedirect
+    return NS_OK;
+  }
+
+  if (succeeded && !mActiveChannel->mIPCClosed) {
+    // TODO: check return value: assume child dead if failed
+    mActiveChannel->SendRedirect3Complete();
+  }
+
+  HttpChannelParent* channelToDelete;
+  if (succeeded) {
+    // Switch to redirect channel and delete the old one.
+    channelToDelete = mActiveChannel;
+    mActiveChannel = mRedirectChannel;
+  } else {
+    // Delete the redirect target channel: continue using old channel
+    channelToDelete = mRedirectChannel;
+  }
+
+  if (!channelToDelete->mIPCClosed)
+    HttpChannelParent::Send__delete__(channelToDelete);
+  mRedirectChannel = nsnull;
+
   return NS_OK;
 }
 
-
 }} // mozilla::net
-
copy from netwerk/protocol/http/HttpChannelParent.h
copy to netwerk/protocol/http/HttpChannelParentListener.h
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParentListener.h
@@ -18,89 +18,77 @@
  *
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
+ *   Honza Bambas <honzab@firemni.cz>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#ifndef mozilla_net_HttpChannelParent_h
-#define mozilla_net_HttpChannelParent_h
+#ifndef mozilla_net_HttpChannelCallbackWrapper_h
+#define mozilla_net_HttpChannelCallbackWrapper_h
 
 #include "nsHttp.h"
-#include "mozilla/dom/PBrowserParent.h"
-#include "mozilla/net/PHttpChannelParent.h"
 #include "mozilla/net/NeckoCommon.h"
+#include "PHttpChannelParams.h"
 #include "nsIStreamListener.h"
 #include "nsIInterfaceRequestor.h"
+#include "nsIChannelEventSink.h"
+#include "nsIRedirectResultListener.h"
 #include "nsIProgressEventSink.h"
-#include "nsITabParent.h"
 
 using namespace mozilla::dom;
 
 class nsICacheEntryDescriptor;
 
 namespace mozilla {
 namespace net {
 
-// Header file contents
-class HttpChannelParent : public PHttpChannelParent
-                        , public nsIStreamListener
-                        , public nsIInterfaceRequestor
-                        , public nsIProgressEventSink
+class HttpChannelParent;
+
+class HttpChannelParentListener : public nsIInterfaceRequestor
+                                 , public nsIChannelEventSink
+                                 , public nsIRedirectResultListener
+                                 , public nsIStreamListener
 {
 public:
   NS_DECL_ISUPPORTS
+  NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIREDIRECTRESULTLISTENER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIINTERFACEREQUESTOR
-  NS_DECL_NSIPROGRESSEVENTSINK
 
-  HttpChannelParent(PBrowserParent* iframeEmbedding);
-  virtual ~HttpChannelParent();
+  HttpChannelParentListener(HttpChannelParent* aInitialChannel);
+  virtual ~HttpChannelParentListener();
 
 protected:
-  virtual bool RecvAsyncOpen(const IPC::URI&            uri,
-                             const IPC::URI&            originalUri,
-                             const IPC::URI&            docUri,
-                             const IPC::URI&            referrerUri,
-                             const PRUint32&            loadFlags,
-                             const RequestHeaderTuples& requestHeaders,
-                             const nsHttpAtom&          requestMethod,
-                             const nsCString&           uploadStreamData,
-                             const PRInt32&             uploadStreamInfo,
-                             const PRUint16&            priority,
-                             const PRUint8&             redirectionLimit,
-                             const PRBool&              allowPipelining,
-                             const PRBool&              forceAllowThirdPartyCookie);
-
-  virtual bool RecvSetPriority(const PRUint16& priority);
-  virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
-
-  virtual void ActorDestroy(ActorDestroyReason why);
+  friend class HttpChannelParent;
+  void OnContentRedirectResultReceived(
+                            const nsresult result, 
+                            const RequestHeaderTuples& changedHeaders);
 
 private:
-  nsCOMPtr<nsITabParent> mTabParent;
-  nsCOMPtr<nsIChannel> mChannel;
-  nsCOMPtr<nsICacheEntryDescriptor> mCacheDescriptor;
-  bool mIPCClosed;                // PHttpChannel actor has been Closed()
+  nsRefPtr<HttpChannelParent> mActiveChannel;
+  nsRefPtr<HttpChannelParent> mRedirectChannel;
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_HttpChannelParent_h
--- a/netwerk/protocol/http/Makefile.in
+++ b/netwerk/protocol/http/Makefile.in
@@ -109,16 +109,17 @@ CPPSRCS = \
   nsHttpActivityDistributor.cpp \
   nsHttpChannelAuthProvider.cpp \
   $(NULL)
 
 ifdef MOZ_IPC
 CPPSRCS += \
   HttpChannelParent.cpp \
   HttpChannelChild.cpp \
+  HttpChannelParentListener.cpp \
   $(NULL)
 endif
 
 LOCAL_INCLUDES = \
   -I$(srcdir)/../../base/src \
   -I$(topsrcdir)/xpcom/ds \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/content/events/src \
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -18,16 +18,17 @@
  *
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jason Duell <jduell.mcbugs@gmail.com>
+ *   Honza Bambas <honzab@firemni.cz>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -52,18 +53,16 @@ namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 protocol PHttpChannel
 {
   manager PNecko;
 
 parent:
-  __delete__();
-
   AsyncOpen(URI                 uri,
             // - TODO: bug 571161: unclear if any HTTP channel clients ever
             // set originalURI != uri (about:credits?); also not clear if
             // chrome channel would ever need to know.  Get rid of next arg?
             URI                 original,
             URI                 doc,
             URI                 referrer,
             PRUint32            loadFlags,
@@ -75,16 +74,19 @@ parent:
             PRUint8             redirectionLimit,
             PRBool              allowPipelining,
             PRBool              forceAllowThirdPartyCookie);
 
   SetPriority(PRUint16 priority);
 
   SetCacheTokenCachedCharset(nsCString charset);
 
+  // Reports approval/veto of redirect by child process redirect observers
+  Redirect2Result(nsresult result, RequestHeaderTuples changedHeaders);
+
 child:
   OnStartRequest(nsHttpResponseHead responseHead,
                  PRBool             useResponseHead,
                  PRBool             isFromCache,
                  PRBool             cacheEntryAvailable,
                  PRUint32           cacheExpirationTime,
                  nsCString          cachedCharset);
 
@@ -92,14 +94,26 @@ child:
                   PRUint32  offset, 
                   PRUint32  count);
 
   OnStopRequest(nsresult statusCode);
 
   OnProgress(PRUint64 progress, PRUint64 progressMax);
 
   OnStatus(nsresult status, nsString statusArg);
+
+  // Called to initiate content channel redirect, starts talking to sinks
+  // on the content process and reports result via OnRedirect2Result above
+  Redirect1Begin(PHttpChannel       newChannel,
+                 URI                newUri,
+                 PRUint32           redirectFlags,
+                 nsHttpResponseHead responseHead);
+  // Called if redirect successful so that child can complete setup.
+  Redirect3Complete();
+
+both:
+  __delete__();
 };
 
 
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -64,24 +64,53 @@
 #include "prprf.h"
 #include "nsEscape.h"
 #include "nsInt64.h"
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
 #include "nsICacheService.h"
 #include "nsDNSPrefetch.h"
 #include "nsChannelClassifier.h"
+#include "nsIRedirectResultListener.h"
 
 // True if the local cache should be bypassed when processing a request.
 #define BYPASS_LOCAL_CACHE(loadFlags) \
         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
 
 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
 
+class AutoRedirectVetoNotifier
+{
+public:
+    AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel) {}
+    ~AutoRedirectVetoNotifier() {ReportRedirectResult(false);}
+    void DontReport() {mChannel = nsnull;}
+    void RedirectSucceeded() {ReportRedirectResult(true);}
+
+private:
+    nsHttpChannel* mChannel;
+    void ReportRedirectResult(bool succeeded);
+};
+
+void
+AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
+{
+    if (!mChannel)
+        return;
+
+    nsCOMPtr<nsIRedirectResultListener> vetoHook;
+    NS_QueryNotificationCallbacks(mChannel, 
+                                  NS_GET_IID(nsIRedirectResultListener), 
+                                  getter_AddRefs(vetoHook));
+    mChannel = nsnull;
+    if (vetoHook)
+        vetoHook->OnRedirectResult(succeeded);
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel <public>
 //-----------------------------------------------------------------------------
 
 nsHttpChannel::nsHttpChannel()
     : mLogicalOffset(0)
     , mCacheAccess(0)
     , mPostID(0)
@@ -94,19 +123,16 @@ nsHttpChannel::nsHttpChannel()
     , mCachedContentIsPartial(PR_FALSE)
     , mTransactionReplaced(PR_FALSE)
     , mAuthRetryPending(PR_FALSE)
     , mResuming(PR_FALSE)
     , mInitedCacheEntry(PR_FALSE)
     , mCacheForOfflineUse(PR_FALSE)
     , mCachingOpportunistically(PR_FALSE)
     , mFallbackChannel(PR_FALSE)
-    , mInheritApplicationCache(PR_TRUE)
-    , mChooseApplicationCache(PR_FALSE)
-    , mLoadedFromApplicationCache(PR_FALSE)
     , mTracingEnabled(PR_TRUE)
     , mCustomConditionalRequest(PR_FALSE)
     , mFallingBack(PR_FALSE)
     , mWaitingForRedirectCallback(PR_FALSE)
     , mRemoteChannel(PR_FALSE)
     , mRequestTimeInitialized(PR_FALSE)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
@@ -1225,42 +1251,47 @@ nsHttpChannel::AsyncDoReplaceWithProxy(n
 
     PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
 
     if (NS_SUCCEEDED(rv))
         rv = WaitForRedirectCallback();
 
     if (NS_FAILED(rv)) {
+        AutoRedirectVetoNotifier notifier(this);
         PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
         mRedirectChannel = nsnull;
     }
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv)
 {
+    AutoRedirectVetoNotifier notifier(this);
+
     if (NS_FAILED(rv))
         return rv;
 
     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
 
     // Make sure to do this _after_ calling OnChannelRedirect
     mRedirectChannel->SetOriginalURI(mOriginalURI);
 
     // open new channel
     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
     mRedirectChannel = nsnull;
     if (NS_FAILED(rv))
         return rv;
 
     mStatus = NS_BINDING_REDIRECTED;
 
+    notifier.RedirectSucceeded();
+
     // disconnect from the old listeners...
     mListener = nsnull;
     mListenerContext = nsnull;
 
     // ...and the old callbacks
     mCallbacks = nsnull;
     mProgressSink = nsnull;
 
@@ -1588,48 +1619,55 @@ nsHttpChannel::ProcessFallback(PRBool *w
 
     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
 
     if (NS_SUCCEEDED(rv))
         rv = WaitForRedirectCallback();
 
     if (NS_FAILED(rv)) {
+        AutoRedirectVetoNotifier notifier(this);
         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
         mRedirectChannel = nsnull;
         return rv;
     }
 
     // Indicate we are now waiting for the asynchronous redirect callback
     // if all went OK.
     *waitingForRedirectCallback = PR_TRUE;
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::ContinueProcessFallback(nsresult rv)
 {
+    AutoRedirectVetoNotifier notifier(this);
+
     if (NS_FAILED(rv))
         return rv;
 
     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
 
     // Make sure to do this _after_ calling OnChannelRedirect
     mRedirectChannel->SetOriginalURI(mOriginalURI);
 
     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
     mRedirectChannel = nsnull;
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv))
+        return rv;
 
     // close down this channel
     Cancel(NS_BINDING_REDIRECTED);
 
+    notifier.RedirectSucceeded();
+
     // disconnect from our listener
     mListener = 0;
     mListenerContext = 0;
+
     // and from our callbacks
     mCallbacks = nsnull;
     mProgressSink = nsnull;
 
     mFallingBack = PR_TRUE;
 
     return NS_OK;
 }
@@ -2747,161 +2785,52 @@ nsHttpChannel::ClearBogusContentEncoding
         mResponseHead->ClearHeader(nsHttp::Content_Encoding);
     }
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel <redirect>
 //-----------------------------------------------------------------------------
 
-static PLDHashOperator
-CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure)
-{
-    nsIWritablePropertyBag* bag = static_cast<nsIWritablePropertyBag*>
-                                             (aClosure);
-    bag->SetProperty(aKey, aData);
-    return PL_DHASH_NEXT;
-}
-
 nsresult
 nsHttpChannel::SetupReplacementChannel(nsIURI       *newURI, 
                                        nsIChannel   *newChannel,
                                        PRBool        preserveMethod)
 {
     LOG(("nsHttpChannel::SetupReplacementChannel "
          "[this=%p newChannel=%p preserveMethod=%d]",
          this, newChannel, preserveMethod));
-    PRUint32 newLoadFlags = mLoadFlags | LOAD_REPLACE;
-    // if the original channel was using SSL and this channel is not using
-    // SSL, then no need to inhibit persistent caching.  however, if the
-    // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING
-    // set, then allow the flag to apply to the redirected channel as well.
-    // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels,
-    // we only need to check if the original channel was using SSL.
-    if (mConnectionInfo->UsingSSL())
-        newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING;
-
-    // Do not pass along LOAD_CHECK_OFFLINE_CACHE
-    newLoadFlags &= ~LOAD_CHECK_OFFLINE_CACHE;
-
-    newChannel->SetLoadGroup(mLoadGroup); 
-    newChannel->SetNotificationCallbacks(mCallbacks);
-    newChannel->SetLoadFlags(newLoadFlags);
+
+    nsresult rv = HttpBaseChannel::SetupReplacementChannel(newURI, newChannel, preserveMethod);
+    if (NS_FAILED(rv))
+        return rv;
 
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
     if (!httpChannel)
         return NS_OK; // no other options to set
 
-    if (preserveMethod) {
-        nsCOMPtr<nsIUploadChannel> uploadChannel =
-            do_QueryInterface(httpChannel);
-        nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
-            do_QueryInterface(httpChannel);
-        if (mUploadStream && (uploadChannel2 || uploadChannel)) {
-            // rewind upload stream
-            nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
-            if (seekable)
-                seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
-
-            // replicate original call to SetUploadStream...
-            if (uploadChannel2) {
-                const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type);
-                if (!ctype)
-                    ctype = "";
-                const char *clen  = mRequestHead.PeekHeader(nsHttp::Content_Length);
-                PRInt64 len = clen ? nsCRT::atoll(clen) : -1;
-                uploadChannel2->ExplicitSetUploadStream(
-                        mUploadStream,
-                        nsDependentCString(ctype),
-                        len,
-                        nsDependentCString(mRequestHead.Method()),
-                        mUploadStreamHasHeaders);
-            }
-            else {
-                if (mUploadStreamHasHeaders)
-                    uploadChannel->SetUploadStream(mUploadStream, EmptyCString(),
-                                                   -1);
-                else {
-                    const char *ctype =
-                        mRequestHead.PeekHeader(nsHttp::Content_Type);
-                    const char *clen =
-                        mRequestHead.PeekHeader(nsHttp::Content_Length);
-                    if (!ctype) {
-                        ctype = "application/octet-stream";
-                    }
-                    if (clen) {
-                        uploadChannel->SetUploadStream(mUploadStream,
-                                                       nsDependentCString(ctype),
-                                                       atoi(clen));
-                    }
-                }
-            }
-        }
-        // since preserveMethod is true, we need to ensure that the appropriate 
-        // request method gets set on the channel, regardless of whether or not 
-        // we set the upload stream above. This means SetRequestMethod() will
-        // be called twice if ExplicitSetUploadStream() gets called above.
-
-        httpChannel->SetRequestMethod(nsDependentCString(mRequestHead.Method()));
-    }
-    // convey the referrer if one was used for this channel to the next one
-    if (mReferrer)
-        httpChannel->SetReferrer(mReferrer);
-    // convey the mAllowPipelining flag
-    httpChannel->SetAllowPipelining(mAllowPipelining);
-    // convey the new redirection limit
-    httpChannel->SetRedirectionLimit(mRedirectionLimit - 1);
-
+    // transfer the remote flag
     nsHttpChannel *httpChannelImpl = static_cast<nsHttpChannel*>(httpChannel.get());
     httpChannelImpl->SetRemoteChannel(mRemoteChannel);
 
-    nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
-    if (httpInternal) {
-        // convey the mForceAllowThirdPartyCookie flag
-        httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie);
-
-        // update the DocumentURI indicator since we are being redirected.
-        // if this was a top-level document channel, then the new channel
-        // should have its mDocumentURI point to newURI; otherwise, we
-        // just need to pass along our mDocumentURI to the new channel.
-        if (newURI && (mURI == mDocumentURI))
-            httpInternal->SetDocumentURI(newURI);
-        else
-            httpInternal->SetDocumentURI(mDocumentURI);
-    } 
-    
     // convey the mApplyConversion flag (bug 91862)
     nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
     if (encodedChannel)
         encodedChannel->SetApplyConversion(mApplyConversion);
 
     // transfer the resume information
     if (mResuming) {
         nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(newChannel));
         if (!resumableChannel) {
             NS_WARNING("Got asked to resume, but redirected to non-resumable channel!");
             return NS_ERROR_NOT_RESUMABLE;
         }
         resumableChannel->ResumeAt(mStartPos, mEntityID);
     }
 
-    // transfer application cache information
-    nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
-        do_QueryInterface(newChannel);
-    if (appCacheChannel) {
-        appCacheChannel->SetApplicationCache(mApplicationCache);
-        appCacheChannel->SetInheritApplicationCache(mInheritApplicationCache);
-        // We purposely avoid transfering mChooseApplicationCache.
-    }
-
-    // transfer any properties
-    nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel));
-    if (bag)
-        mPropertyHash.EnumerateRead(CopyProperties, bag.get());
-
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::AsyncProcessRedirection(PRUint32 redirectType)
 {
     LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n",
         this, redirectType));
@@ -3029,26 +2958,29 @@ nsHttpChannel::ContinueProcessRedirectio
 
     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
 
     if (NS_SUCCEEDED(rv))
         rv = WaitForRedirectCallback();
 
     if (NS_FAILED(rv)) {
+        AutoRedirectVetoNotifier notifier(this);
         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
         mRedirectChannel = nsnull;
     }
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::ContinueProcessRedirection(nsresult rv)
 {
+    AutoRedirectVetoNotifier notifier(this);
+
     LOG(("ContinueProcessRedirection [rv=%x]\n", rv));
     if (NS_FAILED(rv))
         return rv;
 
     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
 
     // Make sure to do this _after_ calling OnChannelRedirect
     mRedirectChannel->SetOriginalURI(mOriginalURI);
@@ -3071,19 +3003,22 @@ nsHttpChannel::ContinueProcessRedirectio
     mRedirectChannel = nsnull;
 
     if (NS_FAILED(rv))
         return rv;
 
     // close down this channel
     Cancel(NS_BINDING_REDIRECTED);
     
+    notifier.RedirectSucceeded();
+
     // disconnect from our listener
     mListener = 0;
     mListenerContext = 0;
+
     // and from our callbacks
     mCallbacks = nsnull;
     mProgressSink = nsnull;
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel <auth>
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -49,17 +49,16 @@
 #include "nsInputStreamPump.h"
 #include "nsThreadUtils.h"
 #include "nsTArray.h"
 
 #include "nsIHttpEventSink.h"
 #include "nsICachingChannel.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsICacheListener.h"
-#include "nsIApplicationCache.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIEncodedChannel.h"
 #include "nsIStringEnumerator.h"
 #include "nsIPrompt.h"
 #include "nsIResumableChannel.h"
 #include "nsIProtocolProxyCallback.h"
 #include "nsICancelable.h"
 #include "nsIHttpAuthenticableChannel.h"
@@ -201,17 +200,17 @@ private:
 
     // redirection specific methods
     void     HandleAsyncRedirect();
     nsresult ContinueHandleAsyncRedirect(nsresult);
     void     HandleAsyncNotModified();
     void     HandleAsyncFallback();
     nsresult ContinueHandleAsyncFallback(nsresult);
     nsresult PromptTempRedirect();
-    nsresult SetupReplacementChannel(nsIURI *, nsIChannel *, PRBool preserveMethod);
+    virtual nsresult SetupReplacementChannel(nsIURI *, nsIChannel *, PRBool preserveMethod);
 
     // proxy specific methods
     nsresult ProxyFailover();
     nsresult AsyncDoReplaceWithProxy(nsIProxyInfo *);
     nsresult ContinueDoReplaceWithProxy(nsresult);
     void HandleAsyncReplaceWithProxy();
     nsresult ContinueHandleAsyncReplaceWithProxy(nsresult);
     nsresult ResolveProxy();
@@ -264,18 +263,16 @@ private:
     nsCacheAccessMode                 mCacheAccess;
     PRUint32                          mPostID;
     PRUint32                          mRequestTime;
 
     nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
     nsCacheAccessMode                 mOfflineCacheAccess;
     nsCString                         mOfflineCacheClientID;
 
-    nsCOMPtr<nsIApplicationCache>     mApplicationCache;
-
     // auth specific data
     nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;
 
     // Resumable channel specific data
     nsCString                         mEntityID;
     PRUint64                          mStartPos;
 
     // Function pointer that can be set to indicate that we got suspended while
@@ -309,19 +306,16 @@ private:
     PRUint32                          mInitedCacheEntry         : 1;
     PRUint32                          mCacheForOfflineUse       : 1;
     // True if mCacheForOfflineUse was set because we were caching
     // opportunistically.
     PRUint32                          mCachingOpportunistically : 1;
     // True if we are loading a fallback cache entry from the
     // application cache.
     PRUint32                          mFallbackChannel          : 1;
-    PRUint32                          mInheritApplicationCache  : 1;
-    PRUint32                          mChooseApplicationCache   : 1;
-    PRUint32                          mLoadedFromApplicationCache : 1;
     PRUint32                          mTracingEnabled           : 1;
     // True if consumer added its own If-None-Match or If-Modified-Since
     // headers. In such a case we must not override them in the cache code
     // and also we want to pass possible 304 code response through.
     PRUint32                          mCustomConditionalRequest : 1;
     PRUint32                          mFallingBack              : 1;
     PRUint32                          mWaitingForRedirectCallback : 1;
     // True iff this channel is servicing a remote HttpChannelChild
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_event_sink_wrap.js
@@ -0,0 +1,7 @@
+//
+// Run test script in content process instead of chrome (xpcshell's default)
+//
+
+function run_test() {
+  run_test_in_child("../unit/test_event_sink.js");
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_redirect-caching_canceled_wrap.js
@@ -0,0 +1,7 @@
+//
+// Run test script in content process instead of chrome (xpcshell's default) 
+//
+//
+function run_test() {
+ run_test_in_child("../unit/test_redirect-caching_canceled.js");
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_redirect-caching_failure_wrap.js
@@ -0,0 +1,7 @@
+//
+// Run test script in content process instead of chrome (xpcshell's default)
+//
+//
+function run_test() {
+ run_test_in_child("../unit/test_redirect-caching_failure.js");
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_redirect-caching_passing_wrap.js
@@ -0,0 +1,7 @@
+// 
+// Run test script in content process instead of chrome (xpcshell's default) 
+// 
+// 
+function run_test() {
+ run_test_in_child("../unit/test_redirect-caching_passing.js");
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_redirect_canceled_wrap.js
@@ -0,0 +1,7 @@
+// 
+// Run test script in content process instead of chrome (xpcshell's default) 
+// 
+// 
+function run_test() {
+ run_test_in_child("../unit/test_redirect_canceled.js");
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_redirect_failure_wrap.js
@@ -0,0 +1,7 @@
+//
+// Run test script in content process instead of chrome (xpcshell's default)
+//
+
+function run_test() {
+  run_test_in_child("../unit/test_redirect_failure.js");
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_redirect_passing_wrap.js
@@ -0,0 +1,7 @@
+//
+// Run test script in content process instead of chrome (xpcshell's default)
+//
+
+function run_test() {
+  run_test_in_child("../unit/test_redirect_passing.js");
+}