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 id14998
push userjduell@mozilla.com
push dateWed, 11 Aug 2010 08:34:40 +0000
treeherdermozilla-central@0c581111e40b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell
bugs536294
milestone2.0b4pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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");
+}