Bug 546606 - Make redirect API async - part 2; r=honzab.moz,cbiesinger,bjarne,jst sr=jst
authorBjarne <bjarne@runitsoft.com>
Wed, 04 Aug 2010 22:15:55 -0400
changeset 48889 11170037df20c0c701a6d542e73f5baacab78ea3
parent 48888 def49cb243f0943b36882a1259f8b75edf8ead63
child 48890 6690d31dbdaae7d6b25355f566e026e86b8d7847
push idunknown
push userunknown
push dateunknown
reviewershonzab.moz, cbiesinger, bjarne, jst, jst
bugs546606
milestone2.0b4pre
Bug 546606 - Make redirect API async - part 2; r=honzab.moz,cbiesinger,bjarne,jst sr=jst
caps/src/nsScriptSecurityManager.cpp
content/base/src/nsCSPService.cpp
content/base/src/nsContentUtils.cpp
content/base/src/nsCrossSiteListenerProxy.cpp
content/base/src/nsCrossSiteListenerProxy.h
content/base/src/nsObjectLoadingContent.cpp
content/base/src/nsSyncLoadService.cpp
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/html/content/src/nsHTMLMediaElement.cpp
content/media/nsMediaStream.cpp
docshell/base/nsDocShell.cpp
modules/libpr0n/src/imgLoader.cpp
modules/libpr0n/src/imgRequest.cpp
modules/libpr0n/src/imgRequest.h
netwerk/base/public/nsAsyncRedirectVerifyHelper.h
netwerk/base/public/nsIChannelEventSink.idl
netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp
netwerk/base/src/nsIOService.cpp
netwerk/base/src/nsIOService.h
netwerk/base/src/nsIncrementalDownload.cpp
netwerk/base/src/nsPACMan.cpp
netwerk/base/src/nsURIChecker.cpp
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/test/TestProtocols.cpp
netwerk/test/unit/head_channels.js
netwerk/test/unit/test_bug455311.js
netwerk/test/unit/test_event_sink.js
rdf/base/src/nsRDFXMLDataSource.cpp
toolkit/components/places/src/AsyncFaviconHelpers.cpp
toolkit/components/places/tests/mochitest/bug_411966/redirect.js
toolkit/components/places/tests/network/test_history_redirects.js
toolkit/components/search/nsSearchService.js
toolkit/mozapps/shared/CertUtils.jsm
uriloader/base/nsDocLoader.cpp
uriloader/prefetch/nsOfflineCacheUpdate.cpp
uriloader/prefetch/nsPrefetchService.cpp
xpinstall/src/nsXPInstallManager.cpp
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -90,16 +90,17 @@
 #include "nsDOMJSUtils.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsIClassInfo.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIChromeRegistry.h"
 #include "nsPrintfCString.h"
 #include "nsIContentSecurityPolicy.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
 
 nsIIOService    *nsScriptSecurityManager::sIOService = nsnull;
 nsIXPConnect    *nsScriptSecurityManager::sXPConnect = nsnull;
 nsIThreadJSContextStack *nsScriptSecurityManager::sJSContextStack = nsnull;
 nsIStringBundle *nsScriptSecurityManager::sStrBundle = nsnull;
 JSRuntime       *nsScriptSecurityManager::sRuntime   = 0;
@@ -3252,19 +3253,20 @@ nsScriptSecurityManager::CheckXPCPermiss
     //-- Access tests failed
     return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
 /////////////////////////////////////////////
 // Method implementing nsIChannelEventSink //
 /////////////////////////////////////////////
 NS_IMETHODIMP
-nsScriptSecurityManager::OnChannelRedirect(nsIChannel* oldChannel, 
-                                           nsIChannel* newChannel,
-                                           PRUint32 redirFlags)
+nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel, 
+                                                nsIChannel* newChannel,
+                                                PRUint32 redirFlags,
+                                                nsIAsyncVerifyRedirectCallback *cb)
 {
     nsCOMPtr<nsIPrincipal> oldPrincipal;
     GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
 
     nsCOMPtr<nsIURI> newURI;
     newChannel->GetURI(getter_AddRefs(newURI));
     nsCOMPtr<nsIURI> newOriginalURI;
     newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
@@ -3273,17 +3275,22 @@ nsScriptSecurityManager::OnChannelRedire
 
     const PRUint32 flags =
         nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
         nsIScriptSecurityManager::DISALLOW_SCRIPT;
     nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
     if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
         rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
     }
-    return rv;
+
+    if (NS_FAILED(rv))
+        return rv;
+
+    cb->OnRedirectVerifyCallback(NS_OK);
+    return NS_OK;
 }
 
 
 /////////////////////////////////////
 // Method implementing nsIObserver //
 /////////////////////////////////////
 static const char sPrincipalPrefix[] = "capability.principal";
 static const char sPolicyPrefix[] = "capability.policy.";
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -48,16 +48,18 @@
 #include "nsCSPService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIChannelPolicy.h"
 #include "nsIChannelEventSink.h"
 #include "nsIPropertyBag2.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsNetError.h"
 #include "nsChannelProperties.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsAsyncRedirectVerifyHelper.h"
 
 /* Keeps track of whether or not CSP is enabled */
 PRBool CSPService::sCSPEnabled = PR_TRUE;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
@@ -198,20 +200,23 @@ CSPService::ShouldProcess(PRUint32      
             ("COULD NOT get nsINode for location: %s", uriSpec.get()));
     }
 #endif
     return NS_OK;
 }
 
 /* nsIChannelEventSink implementation */
 NS_IMETHODIMP
-CSPService::OnChannelRedirect(nsIChannel *oldChannel,
-                              nsIChannel *newChannel,
-                              PRUint32   flags)
+CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
+                                   nsIChannel *newChannel,
+                                   PRUint32 flags,
+                                   nsIAsyncVerifyRedirectCallback *callback)
 {
+  nsAsyncRedirectAutoCallback autoCallback(callback);
+
   // get the Content Security Policy and load type from the property bag
   nsCOMPtr<nsISupports> policyContainer;
   nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
   if (!props)
     return NS_OK;
 
   props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
                                 NS_GET_IID(nsISupports),
@@ -252,29 +257,32 @@ CSPService::OnChannelRedirect(nsIChannel
                   nsnull,          // nsISupports - extra
                   &aDecision);
 
 #ifdef PR_LOGGING
   if (newUri) {
     nsCAutoString newUriSpec("None");
     newUri->GetSpec(newUriSpec);
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("CSPService::OnChannelRedirect called for %s", newUriSpec.get()));
+           ("CSPService::AsyncOnChannelRedirect called for %s",
+            newUriSpec.get()));
   }
   if (aDecision == 1)
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("CSPService::OnChannelRedirect ALLOWING request."));
+           ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
   else
     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("CSPService::OnChannelRedirect CANCELLING request."));
+           ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
 #endif
 
   // if ShouldLoad doesn't accept the load, cancel the request
-  if (aDecision != 1)
+  if (aDecision != 1) {
+    autoCallback.DontCallback();
     return NS_BINDING_FAILED;
+  }
 
   // the redirect is permitted, so propagate the Content Security Policy
   // and load type to the redirecting channel
   nsresult rv;
   nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel, &rv);
   if (props2)
     props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
                                    channelPolicy);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -157,16 +157,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsIRunnable.h"
 #include "nsDOMJSUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValue.h"
 #include "nsReferencedElement.h"
 #include "nsIUGenCategory.h"
 #include "nsIDragService.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsCPrefetchService.h"
 #include "nsIChromeRegistry.h"
 #include "nsIMIMEHeaderParam.h"
 #include "nsIDOMXULCommandEvent.h"
 #include "nsIDOMAbstractView.h"
 #include "nsIDOMDragEvent.h"
@@ -5144,41 +5145,46 @@ nsContentUtils::GetSameOriginChecker()
 }
 
 
 NS_IMPL_ISUPPORTS2(nsSameOriginChecker,
                    nsIChannelEventSink,
                    nsIInterfaceRequestor)
 
 NS_IMETHODIMP
-nsSameOriginChecker::OnChannelRedirect(nsIChannel *aOldChannel,
-                                       nsIChannel *aNewChannel,
-                                       PRUint32    aFlags)
+nsSameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                            nsIChannel *aNewChannel,
+                                            PRUint32 aFlags,
+                                            nsIAsyncVerifyRedirectCallback *cb)
 {
   NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
-  if (!nsContentUtils::GetSecurityManager()) {
+  if (!nsContentUtils::GetSecurityManager())
     return NS_ERROR_NOT_AVAILABLE;
-  }
 
   nsCOMPtr<nsIPrincipal> oldPrincipal;
   nsContentUtils::GetSecurityManager()->
     GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
 
   nsCOMPtr<nsIURI> newURI;
   aNewChannel->GetURI(getter_AddRefs(newURI));
   nsCOMPtr<nsIURI> newOriginalURI;
   aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
 
   NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
 
   nsresult rv = oldPrincipal->CheckMayLoad(newURI, PR_FALSE);
   if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
     rv = oldPrincipal->CheckMayLoad(newOriginalURI, PR_FALSE);
   }
-  return rv;
+
+  if (NS_FAILED(rv))
+      return rv;
+
+  cb->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
--- a/content/base/src/nsCrossSiteListenerProxy.cpp
+++ b/content/base/src/nsCrossSiteListenerProxy.cpp
@@ -47,18 +47,20 @@
 #include "nsICharsetAlias.h"
 #include "nsMimeTypes.h"
 #include "nsIStreamConverterService.h"
 #include "nsStringStream.h"
 #include "nsParserUtils.h"
 #include "nsGkAtoms.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsXMLHttpRequest.h"
+#include "nsAsyncRedirectVerifyHelper.h"
 
 static PRBool gDisableCORS = PR_FALSE;
 static PRBool gDisableCORSPrivateData = PR_FALSE;
 
 class nsChannelCanceller
 {
 public:
   nsChannelCanceller(nsIChannel* aChannel)
@@ -76,19 +78,19 @@ public:
   {
     mChannel = nsnull;
   }
 
 private:
   nsIChannel* mChannel;
 };
 
-NS_IMPL_ISUPPORTS4(nsCrossSiteListenerProxy, nsIStreamListener,
+NS_IMPL_ISUPPORTS5(nsCrossSiteListenerProxy, nsIStreamListener,
                    nsIRequestObserver, nsIChannelEventSink,
-                   nsIInterfaceRequestor)
+                   nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback)
 
 /* static */
 void
 nsCrossSiteListenerProxy::Startup()
 {
   nsContentUtils::AddBoolPrefVarCache("content.cors.disable", &gDisableCORS);
   nsContentUtils::AddBoolPrefVarCache("content.cors.no_private_data", &gDisableCORSPrivateData);
 }
@@ -365,49 +367,84 @@ nsCrossSiteListenerProxy::GetInterface(c
   }
 
   return mOuterNotificationCallbacks ?
     mOuterNotificationCallbacks->GetInterface(aIID, aResult) :
     NS_ERROR_NO_INTERFACE;
 }
 
 NS_IMETHODIMP
-nsCrossSiteListenerProxy::OnChannelRedirect(nsIChannel *aOldChannel,
-                                            nsIChannel *aNewChannel,
-                                            PRUint32    aFlags)
+nsCrossSiteListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                                 nsIChannel *aNewChannel,
+                                                 PRUint32 aFlags,
+                                                 nsIAsyncVerifyRedirectCallback *cb)
 {
-  nsChannelCanceller canceller(aOldChannel);
   nsresult rv;
   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
     rv = CheckRequestApproved(aOldChannel, PR_TRUE);
     if (NS_FAILED(rv)) {
       if (nsXMLHttpRequest::sAccessControlCache) {
         nsCOMPtr<nsIURI> oldURI;
         NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
         if (oldURI) {
           nsXMLHttpRequest::sAccessControlCache->
             RemoveEntries(oldURI, mRequestingPrincipal);
         }
       }
+      aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
       return NS_ERROR_DOM_BAD_URI;
     }
   }
 
+  // Prepare to receive callback
+  mRedirectCallback = cb;
+  mOldRedirectChannel = aOldChannel;
+  mNewRedirectChannel = aNewChannel;
+
   nsCOMPtr<nsIChannelEventSink> outer =
     do_GetInterface(mOuterNotificationCallbacks);
   if (outer) {
-    rv = outer->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this);
+    if (NS_FAILED(rv)) {
+        aOldChannel->Cancel(rv); // is this necessary...?
+        mRedirectCallback = nsnull;
+        mOldRedirectChannel = nsnull;
+        mNewRedirectChannel = nsnull;
+    }
+    return rv;  
   }
 
-  rv = UpdateChannel(aNewChannel);
-  NS_ENSURE_SUCCESS(rv, rv);
+  (void) OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCrossSiteListenerProxy::OnRedirectVerifyCallback(nsresult result)
+{
+  NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
+  NS_ASSERTION(mOldRedirectChannel, "mOldRedirectChannel not set in callback");
+  NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
 
-  canceller.DontCancel();
-  
+  if (NS_SUCCEEDED(result)) {
+      nsresult rv = UpdateChannel(mNewRedirectChannel);
+      if (NS_FAILED(rv)) {
+          NS_WARNING("nsCrossSiteListenerProxy::OnRedirectVerifyCallback: "
+                     "UpdateChannel() returned failure");
+      }
+      result = rv;
+  }
+
+  if (NS_FAILED(result)) {
+    mOldRedirectChannel->Cancel(result);
+  }
+
+  mOldRedirectChannel = nsnull;
+  mNewRedirectChannel = nsnull;
+  mRedirectCallback->OnRedirectVerifyCallback(result);
+  mRedirectCallback   = nsnull;
   return NS_OK;
 }
 
 nsresult
 nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)
 {
   nsCOMPtr<nsIURI> uri, originalURI;
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
--- a/content/base/src/nsCrossSiteListenerProxy.h
+++ b/content/base/src/nsCrossSiteListenerProxy.h
@@ -41,27 +41,29 @@
 #include "nsIStreamListener.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIURI.h"
 #include "nsTArray.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 class nsIURI;
 class nsIParser;
 class nsIPrincipal;
 
 extern PRBool
 IsValidHTTPToken(const nsCSubstring& aToken);
 
 class nsCrossSiteListenerProxy : public nsIStreamListener,
                                  public nsIInterfaceRequestor,
-                                 public nsIChannelEventSink
+                                 public nsIChannelEventSink,
+                                 public nsIAsyncVerifyRedirectCallback
 {
 public:
   nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
                            nsIPrincipal* aRequestingPrincipal,
                            nsIChannel* aChannel,
                            PRBool aWithCredentials,
                            nsresult* aResult);
   nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
@@ -72,16 +74,17 @@ public:
                            const nsTArray<nsCString>& aPreflightHeaders,
                            nsresult* aResult);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
 
   // Must be called at startup.
   static void Startup();
 
   void AllowHTTPResult(PRUint32 aResultCode)
   {
     mAllowedHTTPErrors.AppendElement(aResultCode);
   }
@@ -95,11 +98,14 @@ private:
   nsCOMPtr<nsIInterfaceRequestor> mOuterNotificationCallbacks;
   PRBool mWithCredentials;
   PRBool mRequestApproved;
   PRBool mHasBeenCrossSite;
   PRBool mIsPreflight;
   nsCString mPreflightMethod;
   nsTArray<nsCString> mPreflightHeaders;
   nsTArray<PRUint32> mAllowedHTTPErrors;
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  nsCOMPtr<nsIChannel> mOldRedirectChannel;
+  nsCOMPtr<nsIChannel> mNewRedirectChannel;
 };
 
 #endif
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -64,16 +64,17 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamConverterService.h"
 #include "nsIURILoader.h"
 #include "nsIURL.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebNavigationInfo.h"
 #include "nsIScriptChannel.h"
 #include "nsIBlocklistService.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 #include "nsPluginError.h"
 
 // Util headers
 #include "prlog.h"
 
 #include "nsAutoPtr.h"
 #include "nsCURILoader.h"
@@ -1020,26 +1021,28 @@ nsObjectLoadingContent::GetInterface(con
     NS_ADDREF(sink);
     return NS_OK;
   }
   return NS_NOINTERFACE;
 }
 
 // nsIChannelEventSink
 NS_IMETHODIMP
-nsObjectLoadingContent::OnChannelRedirect(nsIChannel *aOldChannel,
-                                          nsIChannel *aNewChannel,
-                                          PRUint32    aFlags)
+nsObjectLoadingContent::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                               nsIChannel *aNewChannel,
+                                               PRUint32 aFlags,
+                                               nsIAsyncVerifyRedirectCallback *cb)
 {
   // If we're already busy with a new load, cancel the redirect
   if (aOldChannel != mChannel) {
     return NS_BINDING_ABORTED;
   }
 
   mChannel = aNewChannel;
+  cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 // <public>
 PRInt32
 nsObjectLoadingContent::ObjectState() const
 {
   switch (mType) {
--- a/content/base/src/nsSyncLoadService.cpp
+++ b/content/base/src/nsSyncLoadService.cpp
@@ -40,16 +40,17 @@
  * A service that provides methods for synchronously loading a DOM in various ways.
  */
 
 #include "nsSyncLoadService.h"
 #include "nsCOMPtr.h"
 #include "nsIChannel.h"
 #include "nsIDOMLoadListener.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
@@ -355,24 +356,26 @@ nsSyncLoader::Error(nsIDOMEvent* aEvent)
     if (mLoading) {
         mLoading = PR_FALSE;
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSyncLoader::OnChannelRedirect(nsIChannel *aOldChannel,
-                                nsIChannel *aNewChannel,
-                                PRUint32    aFlags)
+nsSyncLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                     nsIChannel *aNewChannel,
+                                     PRUint32 aFlags,
+                                     nsIAsyncVerifyRedirectCallback *callback)
 {
     NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
 
     mChannel = aNewChannel;
 
+    callback->OnRedirectVerifyCallback(NS_OK);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSyncLoader::GetInterface(const nsIID & aIID,
                            void **aResult)
 {
     return QueryInterface(aIID, aResult);
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -94,16 +94,17 @@
 #include "nsIStorageStream.h"
 #include "nsIPromptFactory.h"
 #include "nsIWindowWatcher.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsIConsoleService.h"
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
+#include "nsAsyncRedirectVerifyHelper.h"
 
 #define LOAD_STR "load"
 #define ERROR_STR "error"
 #define ABORT_STR "abort"
 #define LOADSTART_STR "loadstart"
 #define PROGRESS_STR "progress"
 #define UPLOADPROGRESS_STR "uploadprogress"
 #define READYSTATE_STR "readystatechange"
@@ -481,23 +482,27 @@ nsACProxyListener::OnDataAvailable(nsIRe
                                    nsIInputStream *inStr,
                                    PRUint32 sourceOffset,
                                    PRUint32 count)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsACProxyListener::OnChannelRedirect(nsIChannel *aOldChannel,
-                                     nsIChannel *aNewChannel,
-                                     PRUint32 aFlags)
+nsACProxyListener::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                          nsIChannel *aNewChannel,
+                                          PRUint32 aFlags,
+                                          nsIAsyncVerifyRedirectCallback *callback)
 {
   // Only internal redirects allowed for now.
-  return NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) ?
-         NS_OK : NS_ERROR_DOM_BAD_URI;
+  if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags))
+    return NS_ERROR_DOM_BAD_URI;
+
+  callback->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsACProxyListener::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
@@ -2983,54 +2988,129 @@ nsXMLHttpRequest::ChangeState(PRUint32 a
     NS_ENSURE_SUCCESS(rv, rv);
 
     DispatchDOMEvent(nsnull, event, nsnull, nsnull);
   }
 
   return rv;
 }
 
+/*
+ * Simple helper class that just forwards the redirect callback back
+ * to the nsXMLHttpRequest.
+ */
+class AsyncVerifyRedirectCallbackForwarder : public nsIAsyncVerifyRedirectCallback
+{
+public:
+  AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest *xhr)
+    : mXHR(xhr)
+  {
+  }
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
+
+  // nsIAsyncVerifyRedirectCallback implementation
+  NS_IMETHOD OnRedirectVerifyCallback(nsresult result)
+  {
+    mXHR->OnRedirectVerifyCallback(result);
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsXMLHttpRequest> mXHR;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncVerifyRedirectCallbackForwarder)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mXHR, nsIDOMEventListener)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncVerifyRedirectCallbackForwarder)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXHR)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
+
+
 /////////////////////////////////////////////////////
 // nsIChannelEventSink methods:
 //
 NS_IMETHODIMP
-nsXMLHttpRequest::OnChannelRedirect(nsIChannel *aOldChannel,
-                                    nsIChannel *aNewChannel,
-                                    PRUint32    aFlags)
+nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                         nsIChannel *aNewChannel,
+                                         PRUint32    aFlags,
+                                         nsIAsyncVerifyRedirectCallback *callback)
 {
   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
 
   nsresult rv;
 
   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
     rv = CheckChannelForCrossSiteRequest(aNewChannel);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("nsXMLHttpRequest::OnChannelRedirect: "
+                 "CheckChannelForCrossSiteRequest returned failure");
+      return rv;
+    }
 
     // Disable redirects for preflighted cross-site requests entirely for now
     // Note, do this after the call to CheckChannelForCrossSiteRequest
     // to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
     if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
        return NS_ERROR_DOM_BAD_URI;
     }
   }
 
+  // Prepare to receive callback
+  mRedirectCallback = callback;
+  mNewRedirectChannel = aNewChannel;
+
   if (mChannelEventSink) {
-    rv =
-      mChannelEventSink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
+    nsRefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
+      new AsyncVerifyRedirectCallbackForwarder(this);
+
+    rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
+                                                   aNewChannel,
+                                                   aFlags, fwd);
     if (NS_FAILED(rv)) {
-      mErrorLoad = PR_TRUE;
-      return rv;
+        mRedirectCallback = nsnull;
+        mNewRedirectChannel = nsnull;
     }
+    return rv;
   }
-
-  mChannel = aNewChannel;
-
+  OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
+void
+nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
+{
+  NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
+  NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
+
+  if (NS_SUCCEEDED(result))
+    mChannel = mNewRedirectChannel;
+  else
+    mErrorLoad = PR_TRUE;
+
+  mNewRedirectChannel = nsnull;
+
+  mRedirectCallback->OnRedirectVerifyCallback(result);
+  mRedirectCallback = nsnull;
+}
+
 /////////////////////////////////////////////////////
 // nsIProgressEventSink methods:
 //
 
 NS_IMETHODIMP
 nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, PRUint64 aProgress, PRUint64 aProgressMax)
 {
   // We're in middle of processing multipart headers and we don't want to report
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -46,16 +46,17 @@
 #include "nsIURI.h"
 #include "nsIHttpChannel.h"
 #include "nsIDocument.h"
 #include "nsIStreamListener.h"
 #include "nsWeakReference.h"
 #include "jsapi.h"
 #include "nsIScriptContext.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIProgressEventSink.h"
 #include "nsCOMArray.h"
 #include "nsJSUtils.h"
 #include "nsTArray.h"
 #include "nsIJSNativeInitializer.h"
 #include "nsIDOMLSProgressEvent.h"
@@ -65,16 +66,17 @@
 #include "prtime.h"
 #include "nsIDOMNSEvent.h"
 #include "nsITimer.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsDOMProgressEvent.h"
 #include "nsDOMEventTargetWrapperCache.h"
 
 class nsILoadGroup;
+class AsyncVerifyRedirectCallbackForwarder;
 
 class nsAccessControlLRUCache
 {
 public:
   struct TokenTime
   {
     nsCString token;
     PRTime expirationTime;
@@ -335,16 +337,19 @@ protected:
    * inappropriate headers are set, and no username/password is set.
    *
    * Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
    */
   nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
 
   void StartProgressEventTimer();
 
+  friend class AsyncVerifyRedirectCallbackForwarder;
+  void OnRedirectVerifyCallback(nsresult result);
+
   nsCOMPtr<nsISupports> mContext;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIChannel> mChannel;
   // mReadRequest is different from mChannel for multipart requests
   nsCOMPtr<nsIRequest> mReadRequest;
   nsCOMPtr<nsIDOMDocument> mResponseXML;
   nsCOMPtr<nsIChannel> mACGetChannel;
   nsTArray<nsCString> mACUnsafeHeaders;
@@ -408,16 +413,19 @@ protected:
 
   PRPackedBool mTimerIsActive;
   PRPackedBool mProgressEventWasDelayed;
   PRPackedBool mLoadLengthComputable;
   PRUint64 mLoadTotal; // 0 if not known.
   nsCOMPtr<nsITimer> mProgressNotifier;
 
   PRPackedBool mFirstStartRequestSeen;
+  
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  nsCOMPtr<nsIChannel> mNewRedirectChannel;
 };
 
 // helper class to expose a progress DOM Event
 
 class nsXMLHttpProgressEvent : public nsIDOMProgressEvent,
                                public nsIDOMLSProgressEvent,
                                public nsIDOMNSEvent,
                                public nsIPrivateDOMEvent
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -81,16 +81,17 @@
 #include "nsCrossSiteListenerProxy.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsICachingChannel.h"
 #include "nsLayoutUtils.h"
 #include "nsVideoFrame.h"
 #include "BasicLayers.h"
 #include <limits>
 #include "nsIDocShellTreeItem.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 #ifdef MOZ_OGG
 #include "nsOggDecoder.h"
 #endif
 #ifdef MOZ_WAVE
 #include "nsWaveDecoder.h"
 #endif
 #ifdef MOZ_WEBM
@@ -347,25 +348,29 @@ NS_IMETHODIMP nsHTMLMediaElement::MediaL
 {
   if (!mNextListener) {
     NS_ERROR("Must have a chained listener; OnStartRequest should have canceled this request");
     return NS_BINDING_ABORTED;
   }
   return mNextListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount);
 }
 
-NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnChannelRedirect(nsIChannel* aOldChannel,
-                                                                       nsIChannel* aNewChannel,
-                                                                       PRUint32 aFlags)
+NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+                                                                            nsIChannel* aNewChannel,
+                                                                            PRUint32 aFlags,
+                                                                            nsIAsyncVerifyRedirectCallback* cb)
 {
+  // TODO is this really correct?? See bug #579329.
   if (mElement)
     mElement->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
   nsCOMPtr<nsIChannelEventSink> sink = do_QueryInterface(mNextListener);
   if (sink)
-    return sink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
+    return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
+
+  cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
--- a/content/media/nsMediaStream.cpp
+++ b/content/media/nsMediaStream.cpp
@@ -51,16 +51,17 @@
 #include "nsIStreamListener.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsHTMLMediaElement.h"
 #include "nsIDocument.h"
 #include "nsDOMError.h"
 #include "nsICachingChannel.h"
 #include "nsURILoader.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 #define HTTP_OK_CODE 200
 #define HTTP_PARTIAL_RESPONSE_CODE 206
 
 using mozilla::TimeStamp;
 
 nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder,
     nsIChannel* aChannel, nsIURI* aURI)
@@ -121,23 +122,30 @@ nsMediaChannelStream::Listener::OnDataAv
                                                 PRUint32 aCount)
 {
   if (!mStream)
     return NS_OK;
   return mStream->OnDataAvailable(aRequest, aStream, aCount);
 }
 
 nsresult
-nsMediaChannelStream::Listener::OnChannelRedirect(nsIChannel* aOldChannel,
-                                                  nsIChannel* aNewChannel,
-                                                  PRUint32 aFlags)
+nsMediaChannelStream::Listener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+                                                       nsIChannel* aNewChannel,
+                                                       PRUint32 aFlags,
+                                                       nsIAsyncVerifyRedirectCallback* cb)
 {
-  if (!mStream)
-    return NS_OK;
-  return mStream->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
+  nsresult rv = NS_OK;
+  if (mStream)
+    rv = mStream->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
+
+  if (NS_FAILED(rv))
+    return rv;
+
+  cb->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
 }
 
 nsresult
 nsMediaChannelStream::Listener::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -80,16 +80,17 @@
 #include "nsPoint.h"
 #include "nsGfxCIID.h"
 #include "nsIObserverService.h"
 #include "nsIPrompt.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 #include "nsTextFormatter.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIUploadChannel.h"
 #include "nsISecurityEventSink.h"
 #include "mozilla/FunctionTimer.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIJSContextStack.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsDocumentCharsetInfoCID.h"
 #include "nsIScrollableFrame.h"
@@ -497,37 +498,41 @@ nsPingListener::GetInterface(const nsIID
     *result = (nsIChannelEventSink *) this;
     return NS_OK;
   }
 
   return NS_ERROR_NO_INTERFACE;
 }
 
 NS_IMETHODIMP
-nsPingListener::OnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
-                                  PRUint32 flags)
+nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
+                                       PRUint32 flags,
+                                       nsIAsyncVerifyRedirectCallback *callback)
 {
   nsCOMPtr<nsIURI> newURI;
   newChan->GetURI(getter_AddRefs(newURI));
 
   if (!CheckPingURI(newURI, mContent))
     return NS_ERROR_ABORT;
 
-  if (!mRequireSameHost)
-    return NS_OK;
+  if (!mRequireSameHost) {
+    callback->OnRedirectVerifyCallback(NS_OK);
+    return NS_OK;
+  }
 
   // XXXbz should this be using something more like the nsContentUtils
   // same-origin checker?
   nsCOMPtr<nsIURI> oldURI;
   oldChan->GetURI(getter_AddRefs(oldURI));
   NS_ENSURE_STATE(oldURI && newURI);
 
   if (!IsSameHost(oldURI, newURI))
     return NS_ERROR_ABORT;
 
+  callback->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 struct SendPingInfo {
   PRInt32 numPings;
   PRInt32 maxPings;
   PRBool  requireSameHost;
   nsIURI *referrer;
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -47,16 +47,17 @@
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsICachingChannel.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIPrefBranch2.h"
 #include "nsIPrefService.h"
 #include "nsIProgressEventSink.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIProxyObjectManager.h"
 #include "nsIServiceManager.h"
 #include "nsIFileURL.h"
 #include "nsThreadUtils.h"
 #include "nsXPIDLString.h"
 #include "nsCRT.h"
 
 #include "netCore.h"
@@ -314,37 +315,42 @@ nsProgressNotificationProxy::OnStatus(ns
                                 NS_GET_IID(nsIProgressEventSink),
                                 getter_AddRefs(target));
   if (!target)
     return NS_OK;
   return target->OnStatus(mImageRequest, ctxt, status, statusArg);
 }
 
 NS_IMETHODIMP
-nsProgressNotificationProxy::OnChannelRedirect(nsIChannel *oldChannel,
-                                               nsIChannel *newChannel,
-                                               PRUint32 flags) {
+nsProgressNotificationProxy::AsyncOnChannelRedirect(nsIChannel *oldChannel,
+                                                    nsIChannel *newChannel,
+                                                    PRUint32 flags,
+                                                    nsIAsyncVerifyRedirectCallback *cb) {
   // The 'old' channel should match the current one
   NS_ABORT_IF_FALSE(oldChannel == mChannel,
                     "old channel doesn't match current!");
 
   // Save the new channel
   mChannel = newChannel;
 
   // Tell the original original callbacks about it too
   nsCOMPtr<nsILoadGroup> loadGroup;
   mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   nsCOMPtr<nsIChannelEventSink> target;
   NS_QueryNotificationCallbacks(mOriginalCallbacks,
                                 loadGroup,
                                 NS_GET_IID(nsIChannelEventSink),
                                 getter_AddRefs(target));
-  if (!target)
-    return NS_OK;
-  return target->OnChannelRedirect(oldChannel, newChannel, flags);
+  if (!target) {
+      cb->OnRedirectVerifyCallback(NS_OK);
+      return NS_OK;
+  }
+
+  // Delegate to |target| if set, reusing |cb|
+  return target->AsyncOnChannelRedirect(oldChannel, newChannel, flags, cb);
 }
 
 NS_IMETHODIMP
 nsProgressNotificationProxy::GetInterface(const nsIID& iid,
                                           void** result) {
   if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) {
     *result = static_cast<nsIProgressEventSink*>(this);
     NS_ADDREF_THIS();
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -78,16 +78,17 @@
 #include "plstr.h" // PL_strcasestr(...)
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 
 #include "nsIPrefService.h"
 #include "nsIPrefBranch2.h"
 
 #include "imgDiscardTracker.h"
+#include "nsAsyncRedirectVerifyHelper.h"
 
 #define DISCARD_PREF "image.mem.discardable"
 #define DECODEONDRAW_PREF "image.mem.decodeondraw"
 
 /* Kept up to date by a pref observer. */
 static PRBool gDecodeOnDraw = PR_FALSE;
 static PRBool gDiscardable = PR_FALSE;
 
@@ -153,22 +154,23 @@ imgRequestPrefObserver::Observe(nsISuppo
 
   return NS_OK;
 }
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
 #endif
 
-NS_IMPL_ISUPPORTS7(imgRequest,
+NS_IMPL_ISUPPORTS8(imgRequest,
                    imgIDecoderObserver, imgIContainerObserver,
                    nsIStreamListener, nsIRequestObserver,
                    nsISupportsWeakReference,
                    nsIChannelEventSink,
-                   nsIInterfaceRequestor)
+                   nsIInterfaceRequestor,
+                   nsIAsyncVerifyRedirectCallback)
 
 imgRequest::imgRequest() : 
   mCacheId(0), mValidator(nsnull), mImageSniffers("image-sniffing-services"),
   mDecodeRequested(PR_FALSE), mIsMultiPartChannel(PR_FALSE),
   mGotData(PR_FALSE), mIsInCache(PR_FALSE)
 {}
 
 imgRequest::~imgRequest()
@@ -1114,57 +1116,88 @@ imgRequest::GetInterface(const nsIID & a
     return QueryInterface(aIID, aResult);
 
   NS_ASSERTION(mPrevChannelSink != this, 
                "Infinite recursion - don't keep track of channel sinks that are us!");
   return mPrevChannelSink->GetInterface(aIID, aResult);
 }
 
 /** nsIChannelEventSink methods **/
-
-/* void onChannelRedirect (in nsIChannel oldChannel, in nsIChannel newChannel, in unsigned long flags); */
 NS_IMETHODIMP
-imgRequest::OnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, PRUint32 flags)
+imgRequest::AsyncOnChannelRedirect(nsIChannel *oldChannel,
+                                   nsIChannel *newChannel, PRUint32 flags,
+                                   nsIAsyncVerifyRedirectCallback *callback)
 {
-  NS_ASSERTION(mRequest && mChannel, "Got an OnChannelRedirect after we nulled out mRequest!");
+  NS_ASSERTION(mRequest && mChannel, "Got a channel redirect after we nulled out mRequest!");
   NS_ASSERTION(mChannel == oldChannel, "Got a channel redirect for an unknown channel!");
   NS_ASSERTION(newChannel, "Got a redirect to a NULL channel!");
 
-  nsresult rv = NS_OK;
+  // Prepare for callback
+  mRedirectCallback = callback;
+  mNewRedirectChannel = newChannel;
+
   nsCOMPtr<nsIChannelEventSink> sink(do_GetInterface(mPrevChannelSink));
   if (sink) {
-    rv = sink->OnChannelRedirect(oldChannel, newChannel, flags);
-    if (NS_FAILED(rv))
-      return rv;
+    nsresult rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
+                                               this);
+    if (NS_FAILED(rv)) {
+        mRedirectCallback = nsnull;
+        mNewRedirectChannel = nsnull;
+    }
+    return rv;
+  }
+  
+  (void) OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+imgRequest::OnRedirectVerifyCallback(nsresult result)
+{
+  NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
+  NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
+    
+  if (NS_FAILED(result)) {
+      mRedirectCallback->OnRedirectVerifyCallback(result);
+      mRedirectCallback = nsnull;
+      mNewRedirectChannel = nsnull;
+      return NS_OK;
   }
 
-  mChannel = newChannel;
+  mChannel = mNewRedirectChannel;
+  mNewRedirectChannel = nsnull;
 
   // Don't make any cache changes if we're going to point to the same thing. We
   // compare specs and not just URIs here because URIs that compare as
   // .Equals() might have different hashes.
   nsCAutoString oldspec;
   if (mKeyURI)
     mKeyURI->GetSpec(oldspec);
   LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "old", oldspec.get());
 
   // make sure we have a protocol that returns data rather than opens
   // an external application, e.g. mailto:
   nsCOMPtr<nsIURI> uri;
-  newChannel->GetURI(getter_AddRefs(uri));
+  mChannel->GetURI(getter_AddRefs(uri));
   PRBool doesNotReturnData = PR_FALSE;
-  rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
-                           &doesNotReturnData);
-  if (NS_FAILED(rv))
-    return rv;
-  if (doesNotReturnData)
-    return NS_ERROR_ABORT;
+  nsresult rv =
+    NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
+                        &doesNotReturnData);
+
+  if (NS_SUCCEEDED(rv) && doesNotReturnData)
+    rv = NS_ERROR_ABORT;
+
+  if (NS_FAILED(rv)) {
+    mRedirectCallback->OnRedirectVerifyCallback(rv);
+    mRedirectCallback = nsnull;
+    return NS_OK;
+  }
 
   nsCOMPtr<nsIURI> newURI;
-  newChannel->GetOriginalURI(getter_AddRefs(newURI));
+  mChannel->GetOriginalURI(getter_AddRefs(newURI));
   nsCAutoString newspec;
   if (newURI)
     newURI->GetSpec(newspec);
   LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "new", newspec.get());
 
   if (oldspec != newspec) {
     if (mIsInCache) {
       // Remove the cache entry from the cache, but don't null out mCacheEntry
@@ -1181,10 +1214,12 @@ imgRequest::OnChannelRedirect(nsIChannel
     if (mIsInCache) {
       // If we don't still have a URI or cache entry, we don't want to put
       // ourselves back into the cache.
       if (mKeyURI && mCacheEntry)
         imgLoader::PutIntoCache(mKeyURI, mCacheEntry);
     }
   }
 
-  return rv;
+  mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
+  mRedirectCallback = nsnull;
+  return NS_OK;
 }
--- a/modules/libpr0n/src/imgRequest.h
+++ b/modules/libpr0n/src/imgRequest.h
@@ -57,29 +57,31 @@
 #include "nsCategoryCache.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTObserverArray.h"
 #include "nsWeakReference.h"
 #include "ImageErrors.h"
 #include "imgIRequest.h"
 #include "imgContainer.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 class imgCacheValidator;
 
 class imgRequestProxy;
 class imgCacheEntry;
 class imgMemoryReporter;
 class imgRequestNotifyRunnable;
 
 class imgRequest : public imgIDecoderObserver,
                    public nsIStreamListener,
                    public nsSupportsWeakReference,
                    public nsIChannelEventSink,
-                   public nsIInterfaceRequestor
+                   public nsIInterfaceRequestor,
+                   public nsIAsyncVerifyRedirectCallback
 {
 public:
   imgRequest();
   virtual ~imgRequest();
 
   NS_DECL_ISUPPORTS
 
   nsresult Init(nsIURI *aURI,
@@ -177,16 +179,17 @@ private:
 
 public:
   NS_DECL_IMGIDECODEROBSERVER
   NS_DECL_IMGICONTAINEROBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
 
 private:
   friend class imgMemoryReporter;
 
   nsCOMPtr<nsIRequest> mRequest;
   // The original URI we were loaded with.
   nsCOMPtr<nsIURI> mURI;
   // The URI we are keyed on in the cache.
@@ -206,17 +209,18 @@ private:
 
   void *mCacheId;
 
   void *mLoadId;
   PRTime mLoadTime;
 
   imgCacheValidator *mValidator;
   nsCategoryCache<nsIContentSniffer> mImageSniffers;
-
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  nsCOMPtr<nsIChannel> mNewRedirectChannel;
   // Sometimes consumers want to do things before the image is ready. Let them,
   // and apply the action when the image becomes available.
   PRPackedBool mDecodeRequested : 1;
 
   PRPackedBool mIsMultiPartChannel : 1;
   PRPackedBool mGotData : 1;
   PRPackedBool mIsInCache : 1;
 };
--- a/netwerk/base/public/nsAsyncRedirectVerifyHelper.h
+++ b/netwerk/base/public/nsAsyncRedirectVerifyHelper.h
@@ -1,8 +1,9 @@
+/* -*- Mode: C++; 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/
  *
@@ -15,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *    Honza Bambas <honzab@firemni.cz>
+ *    Bjarne Geir Herland <bjarne@runitsoft.com>
  *
  * 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
@@ -34,34 +36,52 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsAsyncRedirectVerifyHelper_h
 #define nsAsyncRedirectVerifyHelper_h
 
 #include "nsIRunnable.h"
-
+#include "nsIThread.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
 
 class nsIChannel;
 
 /**
  * This class simplifies call of OnChannelRedirect of IOService and
  * the sink bound with the channel being redirected while the result of
  * redirect decision is returned through the callback.
  */
-class nsAsyncRedirectVerifyHelper : public nsIRunnable
+class nsAsyncRedirectVerifyHelper : public nsIRunnable,
+                                    public nsIAsyncVerifyRedirectCallback
 {
     NS_DECL_ISUPPORTS
     NS_DECL_NSIRUNNABLE
+    NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
 
 public:
+    nsAsyncRedirectVerifyHelper();
+
+    /*
+     * Calls AsyncOnChannelRedirect() on the given sink with the given
+     * channels and flags. Keeps track of number of async callbacks to expect.
+     */
+    nsresult DelegateOnChannelRedirect(nsIChannelEventSink *sink,
+                                       nsIChannel *oldChannel, 
+                                       nsIChannel *newChannel,
+                                       PRUint32 flags);
+ 
     /**
-     * Initialize and runs the chain of OnChannelRedirect calls. OldChannel
+     * Initialize and run the chain of AsyncOnChannelRedirect calls. OldChannel
      * is QI'ed for nsIAsyncVerifyRedirectCallback. The result of the redirect
      * decision is passed through this interface back to the oldChannel.
      *
      * @param oldChan
      *    channel being redirected, MUST implement
      *    nsIAsyncVerifyRedirectCallback
      * @param newChan
      *    target of the redirect channel
@@ -76,13 +96,62 @@ public:
                   PRUint32 flags,
                   PRBool synchronize = PR_FALSE);
 
 protected:
     nsCOMPtr<nsIChannel> mOldChan;
     nsCOMPtr<nsIChannel> mNewChan;
     PRUint32 mFlags;
     PRBool mWaitingForRedirectCallback;
+    nsCOMPtr<nsIThread>      mCallbackThread;
+    PRBool                   mCallbackInitiated;
+    PRInt32                  mExpectedCallbacks;
+    nsresult                 mResult; // value passed to callback
 
-    void Callback(nsresult result);
+    void InitCallback();
+    
+    /**
+     * Calls back to |oldChan| as described in Init()
+     */
+    void ExplicitCallback(nsresult result);
+
+private:
+    ~nsAsyncRedirectVerifyHelper();
+    
+    bool IsOldChannelCanceled();
+};
+
+/*
+ * Helper to make the call-stack handle some control-flow for us
+ */
+class nsAsyncRedirectAutoCallback
+{
+public:
+    nsAsyncRedirectAutoCallback(nsIAsyncVerifyRedirectCallback* aCallback)
+        : mCallback(aCallback)
+    {
+        mResult = NS_OK;
+    }
+    ~nsAsyncRedirectAutoCallback()
+    {
+        if (mCallback)
+            mCallback->OnRedirectVerifyCallback(mResult);
+    }
+    /*
+     * Call this is you want it to call back with a different result-code
+     */
+    void SetResult(nsresult aRes)
+    {
+        mResult = aRes;
+    }
+    /*
+     * Call this is you want to avoid the callback
+     */
+    void DontCallback()
+    {
+        mCallback = nsnull;
+    }
+private:
+    nsIAsyncVerifyRedirectCallback* mCallback;
+    nsresult mResult;
 };
 
 #endif
--- a/netwerk/base/public/nsIChannelEventSink.idl
+++ b/netwerk/base/public/nsIChannelEventSink.idl
@@ -19,16 +19,17 @@
  * Netscape Communications.
  * Portions created by the Initial Developer are Copyright (C) 2001
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Gagan Saksena <gagan@netscape.com> (original author)
  *   Darin Fisher <darin@netscape.com>
  *   Christian Biesinger <cbiesinger@web.de>
+ *   Bjarne Geir Herland <bjarne@runitsoft.com>
  *
  * 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
@@ -37,27 +38,27 @@
  * 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"
 
 interface nsIChannel;
+interface nsIAsyncVerifyRedirectCallback;
 
 /**
  * Implement this interface to receive control over various channel events.
  * Channels will try to get this interface from a channel's
  * notificationCallbacks or, if not available there, from the loadGroup's
  * notificationCallbacks.
  *
- * These methods are called before onStartRequest, and should be handled
- * SYNCHRONOUSLY.
+ * These methods are called before onStartRequest.
  */
-[scriptable, uuid(6757d790-2916-498e-aaca-6b668a956875)]
+[scriptable, uuid(a430d870-df77-4502-9570-d46a8de33154)]
 interface nsIChannelEventSink : nsISupports
 {
     /**
      * This is a temporary redirect. New requests for this resource should
      * continue to use the URI of the old channel.
      *
      * The new URI may be identical to the old one.
      */
@@ -77,27 +78,53 @@ interface nsIChannelEventSink : nsISuppo
      * server, but is specific to the channel implementation.
      *
      * The new URI may be identical to the old one.
      */
     const unsigned long REDIRECT_INTERNAL = 1 << 2;
 
     /**
      * Called when a redirect occurs. This may happen due to an HTTP 3xx status
-     * code.
+     * code. The purpose of this method is to notify the sink that a redirect
+     * is about to happen, but also to give the sink the right to veto the
+     * redirect by throwing or passing a failure-code in the callback.
+     *
+     * Note that vetoing the redirect simply means that |newChannel| will not
+     * be opened. It is important to understand that |oldChannel| will continue
+     * loading as if it received a HTTP 200, which includes notifying observers
+     * and possibly display or process content attached to the HTTP response.
+     * If the sink wants to prevent this loading it must explicitly deal with
+     * it, e.g. by calling |oldChannel->Cancel()|
+     *
+     * There is a certain freedom in implementing this method:
+     *
+     * If the return-value indicates success, a callback on |callback| is
+     * required. This callback can be done from within asyncOnChannelRedirect
+     * (effectively making the call synchronous) or at some point later
+     * (making the call asynchronous). Repeat: A callback must be done
+     * if this method returns successfully.
+     *
+     * If the return value indicates error (method throws an exception)
+     * the redirect is vetoed and no callback must be done. Repeat: No
+     * callback must be done if this method throws!
+     *
+     * @see nsIAsyncVerifyRedirectCallback::onRedirectVerifyCallback()
      *
      * @param oldChannel
      *        The channel that's being redirected.
      * @param newChannel
      *        The new channel. This channel is not opened yet.
      * @param flags
      *        Flags indicating the type of redirect. A bitmask consisting
      *        of flags from above.
      *        One of REDIRECT_TEMPORARY and REDIRECT_PERMANENT will always be
      *        set.
+     * @param callback
+     *        Object to inform about the async result of this method
      *
-     * @throw <any> Throwing an exception will cancel the load. No network
-     * request for the new channel will be made.
+     * @throw <any> Throwing an exception will cause the redirect to be
+     *        cancelled
      */
-    void onChannelRedirect(in nsIChannel oldChannel, 
-                           in nsIChannel newChannel,
-                           in unsigned long flags);
+    void asyncOnChannelRedirect(in nsIChannel oldChannel, 
+                                in nsIChannel newChannel,
+                                in unsigned long flags,
+                                in nsIAsyncVerifyRedirectCallback callback);
 };
--- a/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp
+++ b/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp
@@ -1,8 +1,9 @@
+/* -*- Mode: C++; 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/
  *
@@ -15,49 +16,96 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *    Honza Bambas <honzab@firemni.cz>
+ *    Bjarne Geir Herland <bjarnw@runitsoft.com>
  *
  * 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 "prlog.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 
 #include "nsIOService.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 
-NS_IMPL_ISUPPORTS1(nsAsyncRedirectVerifyHelper, nsIRunnable)
+#ifdef PR_LOGGING
+static PRLogModuleInfo *gLog = PR_NewLogModule("nsRedirect");
+#define LOG(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
+#else
+#define LOG(args)
+#endif
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsAsyncRedirectVerifyHelper,
+                              nsIAsyncVerifyRedirectCallback,
+                              nsIRunnable)
+
+class nsAsyncVerifyRedirectCallbackEvent : public nsRunnable {
+public:
+    nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
+                                       nsresult result)
+        : mCallback(cb), mResult(result) {
+    }
+
+    NS_IMETHOD Run()
+    {
+        LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() "
+             "callback to %p with result %x",
+             mCallback.get(), mResult));
+       (void) mCallback->OnRedirectVerifyCallback(mResult);
+       return NS_OK;
+    }
+private:
+    nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
+    nsresult mResult;
+};
+
+nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper()
+    : mCallbackInitiated(PR_FALSE),
+      mExpectedCallbacks(0),
+      mResult(NS_OK)
+{
+}
+
+nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper()
+{
+    NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
+                 "Did not receive all required callbacks!");
+}
 
 nsresult
 nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
                                   PRUint32 flags, PRBool synchronize)
 {
-    mOldChan = oldChan;
-    mNewChan = newChan;
-    mFlags = flags;
+    LOG(("nsAsyncRedirectVerifyHelper::Init() "
+         "oldChan=%p newChan=%p", oldChan, newChan));
+    mOldChan           = oldChan;
+    mNewChan           = newChan;
+    mFlags             = flags;
+    mCallbackThread    = do_GetCurrentThread();
 
     if (synchronize)
       mWaitingForRedirectCallback = PR_TRUE;
 
     nsresult rv;
     rv = NS_DispatchToMainThread(this);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -68,60 +116,177 @@ nsAsyncRedirectVerifyHelper::Init(nsICha
           return NS_ERROR_UNEXPECTED;
         }
       }
     }
 
     return NS_OK;
 }
 
-void
-nsAsyncRedirectVerifyHelper::Callback(nsresult result)
+NS_IMETHODIMP
+nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result)
+{
+    LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
+         "result=%x expectedCBs=%u mResult=%x",
+         result, mExpectedCallbacks, mResult));
+
+    --mExpectedCallbacks;
+
+    // If response indicates failure we may call back immediately
+    if (NS_FAILED(result)) {
+        // We chose to store the first failure-value (as opposed to the last)
+        if (NS_SUCCEEDED(mResult))
+            mResult = result;
+
+        // If InitCallback() has been called, just invoke the callback and
+        // return. Otherwise it will be invoked from InitCallback()
+        if (mCallbackInitiated) {
+            ExplicitCallback(mResult);
+            return NS_OK;
+        }
+    }
+
+    // If the expected-counter is in balance and InitCallback() was called, all
+    // sinks have agreed that the redirect is ok and we can invoke our callback
+    if (mCallbackInitiated && mExpectedCallbacks == 0) {
+        ExplicitCallback(mResult);
+    }
+
+    return NS_OK;
+}
+
+nsresult
+nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink,
+                                                       nsIChannel *oldChannel,
+                                                       nsIChannel *newChannel,
+                                                       PRUint32 flags)
 {
-    // TODO E10S OnRedirectCallback has to be called on the original process
-    nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(do_QueryInterface(mOldChan));
-    NS_ASSERTION(callback, "nsAsyncRedirectVerifyHelper: oldChannel doesn't"
-                           " implement nsIAsyncVerifyRedirectCallback");
+    LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
+         "sink=%p expectedCBs=%u mResult=%x",
+         sink, mExpectedCallbacks, mResult));
+
+    ++mExpectedCallbacks;
+
+    if (IsOldChannelCanceled()) {
+        LOG(("  old channel has been canceled, cancel the redirect by "
+             "emulating OnRedirectVerifyCallback..."));
+        (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED);
+        return NS_BINDING_ABORTED;
+    }
+
+    nsresult rv =
+        sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
+
+    LOG(("  result=%x expectedCBs=%u", rv, mExpectedCallbacks));
+
+    // If the sink returns failure from this call the redirect is vetoed. We
+    // emulate a callback from the sink in this case in order to perform all
+    // the necessary logic.
+    if (NS_FAILED(rv)) {
+        LOG(("  emulating OnRedirectVerifyCallback..."));
+        (void) OnRedirectVerifyCallback(rv);
+    }
+
+    return rv;  // Return the actual status since our caller may need it
+}
+
+void
+nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result)
+{
+    LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
+         "result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x",
+         result, mExpectedCallbacks, mCallbackInitiated, mResult));
+
+    nsCOMPtr<nsIAsyncVerifyRedirectCallback>
+        callback(do_QueryInterface(mOldChan));
 
-    if (callback)
-        callback->OnRedirectVerifyCallback(result);
+    if (!callback || !mCallbackThread) {
+        LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
+             "callback=%p mCallbackThread=%p", callback, mCallbackThread));
+        return;
+    }
+
+    mCallbackInitiated = PR_FALSE;  // reset to ensure only one callback
+    mWaitingForRedirectCallback = PR_FALSE;
 
-    mWaitingForRedirectCallback = PR_FALSE;
+    // Now, dispatch the callback on the event-target which called Init()
+    nsRefPtr<nsIRunnable> event =
+        new nsAsyncVerifyRedirectCallbackEvent(callback, result);
+    if (!event) {
+        NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
+                   "failed creating callback event!");
+        return;
+    }
+    nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+        NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
+                   "failed dispatching callback event!");
+    } else {
+        LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
+             "dispatched callback event=%p", event.get()));
+    }
+   
+}
+
+void
+nsAsyncRedirectVerifyHelper::InitCallback()
+{
+    LOG(("nsAsyncRedirectVerifyHelper::InitCallback() "
+         "expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult));
+
+    mCallbackInitiated = PR_TRUE;
+
+    // Invoke the callback if we are done
+    if (mExpectedCallbacks == 0)
+        ExplicitCallback(mResult);
 }
 
 NS_IMETHODIMP
 nsAsyncRedirectVerifyHelper::Run()
 {
     /* If the channel got canceled after it fired AsyncOnChannelRedirect
-     * (bug 546606) and before we got here, mostly because docloader
-     * load has been canceled, we must completely ignore this notification
-     * and prevent any further notification.
-     *
-     * TODO Bug 546606, this must be checked before every single call!
+     * and before we got here, mostly because docloader load has been canceled,
+     * we must completely ignore this notification and prevent any further
+     * notification.
      */
-    PRBool canceled;
-    nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
-        do_QueryInterface(mOldChan);
-    if (oldChannelInternal) {
-      oldChannelInternal->GetCanceled(&canceled);
-      if (canceled) {
-          Callback(NS_BINDING_ABORTED);
-          return NS_OK;
-      }
+    if (IsOldChannelCanceled()) {
+        ExplicitCallback(NS_BINDING_ABORTED);
+        return NS_OK;
     }
 
     // First, the global observer
     NS_ASSERTION(gIOService, "Must have an IO service at this point");
-    nsresult rv = gIOService->OnChannelRedirect(mOldChan, mNewChan, mFlags);
+    LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
+    nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan,
+                                                     mFlags, this);
     if (NS_FAILED(rv)) {
-        Callback(rv);
+        ExplicitCallback(rv);
         return NS_OK;
     }
 
     // Now, the per-channel observers
     nsCOMPtr<nsIChannelEventSink> sink;
     NS_QueryNotificationCallbacks(mOldChan, sink);
-    if (sink)
-        rv = sink->OnChannelRedirect(mOldChan, mNewChan, mFlags);
+    if (sink) {
+        LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
+        rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
+    }
 
-    Callback(rv);
+    // All invocations to AsyncOnChannelRedirect has been done - call
+    // InitCallback() to flag this
+    InitCallback();
     return NS_OK;
 }
+
+bool
+nsAsyncRedirectVerifyHelper::IsOldChannelCanceled()
+{
+    PRBool canceled;
+    nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
+        do_QueryInterface(mOldChan);
+    if (oldChannelInternal) {
+        oldChannelInternal->GetCanceled(&canceled);
+        if (canceled)
+            return true;
+    }
+
+    return false;
+}
\ No newline at end of file
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -319,37 +319,39 @@ NS_IMPL_THREADSAFE_ISUPPORTS5(nsIOServic
                               nsIIOService2,
                               nsINetUtil,
                               nsIObserver,
                               nsISupportsWeakReference)
 
 ////////////////////////////////////////////////////////////////////////////////
 
 nsresult
-nsIOService::OnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
-                               PRUint32 flags)
+nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
+                                    PRUint32 flags,
+                                    nsAsyncRedirectVerifyHelper *helper)
 {
     nsCOMPtr<nsIChannelEventSink> sink =
         do_GetService(NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID);
     if (sink) {
-        nsresult rv = sink->OnChannelRedirect(oldChan, newChan, flags);
+        nsresult rv = helper->DelegateOnChannelRedirect(sink, oldChan,
+                                                        newChan, flags);
         if (NS_FAILED(rv))
             return rv;
     }
 
     // Finally, our category
     const nsCOMArray<nsIChannelEventSink>& entries =
         mChannelEventSinks.GetEntries();
     PRInt32 len = entries.Count();
     for (PRInt32 i = 0; i < len; ++i) {
-        nsresult rv = entries[i]->OnChannelRedirect(oldChan, newChan, flags);
+        nsresult rv = helper->DelegateOnChannelRedirect(entries[i], oldChan,
+                                                        newChan, flags);
         if (NS_FAILED(rv))
             return rv;
     }
-
     return NS_OK;
 }
 
 nsresult
 nsIOService::CacheProtocolHandler(const char *scheme, nsIProtocolHandler *handler)
 {
     for (unsigned int i=0; i<NS_N(gScheme); i++)
     {
--- a/netwerk/base/src/nsIOService.h
+++ b/netwerk/base/src/nsIOService.h
@@ -52,16 +52,17 @@
 #include "nsIURLParser.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsINetUtil.h"
 #include "nsIChannelEventSink.h"
 #include "nsIContentSniffer.h"
 #include "nsCategoryCache.h"
 #include "nsINetworkLinkService.h"
+#include "nsAsyncRedirectVerifyHelper.h"
 
 #define NS_N(x) (sizeof(x)/sizeof(*x))
 
 // We don't want to expose this observer topic.
 // Intended internal use only for remoting offline/inline events.
 // See Bug 552829
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
@@ -90,18 +91,19 @@ public:
 
     NS_HIDDEN_(nsresult) Init();
     NS_HIDDEN_(nsresult) NewURI(const char* aSpec, nsIURI* aBaseURI,
                                 nsIURI* *result,
                                 nsIProtocolHandler* *hdlrResult);
 
     // Called by channels before a redirect happens. This notifies the global
     // redirect observers.
-    nsresult OnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
-                               PRUint32 flags);
+    nsresult AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
+                                    PRUint32 flags,
+                                    nsAsyncRedirectVerifyHelper *helper);
 
     // Gets the array of registered content sniffers
     const nsCOMArray<nsIContentSniffer>& GetContentSniffers() {
       return mContentSniffers.GetEntries();
     }
 
     PRBool IsOffline() { return mOffline; }
     PRBool IsLinkUp();
--- a/netwerk/base/src/nsIncrementalDownload.cpp
+++ b/netwerk/base/src/nsIncrementalDownload.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIIncrementalDownload.h"
 #include "nsIRequestObserver.h"
 #include "nsIProgressEventSink.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "nsIPropertyBag2.h"
 #include "nsIServiceManager.h"
 #include "nsILocalFile.h"
 #include "nsITimer.h"
 #include "nsInt64.h"
@@ -121,74 +122,80 @@ MakeRangeSpec(const nsInt64 &size, const
 //-----------------------------------------------------------------------------
 
 class nsIncrementalDownload : public nsIIncrementalDownload
                             , public nsIStreamListener
                             , public nsIObserver
                             , public nsIInterfaceRequestor
                             , public nsIChannelEventSink
                             , public nsSupportsWeakReference
+                            , public nsIAsyncVerifyRedirectCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUEST
   NS_DECL_NSIINCREMENTALDOWNLOAD
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
 
   nsIncrementalDownload();
 
 private:
   ~nsIncrementalDownload() {}
   nsresult FlushChunk();
   void     UpdateProgress();
   nsresult CallOnStartRequest();
   void     CallOnStopRequest();
   nsresult StartTimer(PRInt32 interval);
   nsresult ProcessTimeout();
   nsresult ReadCurrentSize();
   nsresult ClearRequestHeader(nsIHttpChannel *channel);
 
-  nsCOMPtr<nsIRequestObserver>   mObserver;
-  nsCOMPtr<nsISupports>          mObserverContext;
-  nsCOMPtr<nsIProgressEventSink> mProgressSink;
-  nsCOMPtr<nsIURI>               mURI;
-  nsCOMPtr<nsIURI>               mFinalURI;
-  nsCOMPtr<nsILocalFile>         mDest;
-  nsCOMPtr<nsIChannel>           mChannel;
-  nsCOMPtr<nsITimer>             mTimer;
-  nsAutoArrayPtr<char>           mChunk;
-  PRInt32                        mChunkLen;
-  PRInt32                        mChunkSize;
-  PRInt32                        mInterval;
-  nsInt64                        mTotalSize;
-  nsInt64                        mCurrentSize;
-  PRUint32                       mLoadFlags;
-  PRInt32                        mNonPartialCount;
-  nsresult                       mStatus;
-  PRPackedBool                   mIsPending;
-  PRPackedBool                   mDidOnStartRequest;
-  PRTime                         mLastProgressUpdate;
+  nsCOMPtr<nsIRequestObserver>             mObserver;
+  nsCOMPtr<nsISupports>                    mObserverContext;
+  nsCOMPtr<nsIProgressEventSink>           mProgressSink;
+  nsCOMPtr<nsIURI>                         mURI;
+  nsCOMPtr<nsIURI>                         mFinalURI;
+  nsCOMPtr<nsILocalFile>                   mDest;
+  nsCOMPtr<nsIChannel>                     mChannel;
+  nsCOMPtr<nsITimer>                       mTimer;
+  nsAutoArrayPtr<char>                     mChunk;
+  PRInt32                                  mChunkLen;
+  PRInt32                                  mChunkSize;
+  PRInt32                                  mInterval;
+  nsInt64                                  mTotalSize;
+  nsInt64                                  mCurrentSize;
+  PRUint32                                 mLoadFlags;
+  PRInt32                                  mNonPartialCount;
+  nsresult                                 mStatus;
+  PRPackedBool                             mIsPending;
+  PRPackedBool                             mDidOnStartRequest;
+  PRTime                                   mLastProgressUpdate;
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  nsCOMPtr<nsIChannel>                     mNewRedirectChannel;
 };
 
 nsIncrementalDownload::nsIncrementalDownload()
   : mChunkLen(0)
   , mChunkSize(DEFAULT_CHUNK_SIZE)
   , mInterval(DEFAULT_INTERVAL)
   , mTotalSize(-1)
   , mCurrentSize(-1)
   , mLoadFlags(LOAD_NORMAL)
   , mNonPartialCount(0)
   , mStatus(NS_OK)
   , mIsPending(PR_FALSE)
   , mDidOnStartRequest(PR_FALSE)
   , mLastProgressUpdate(0)
+  , mRedirectCallback(nsnull)
+  , mNewRedirectChannel(nsnull)
 {
 }
 
 nsresult
 nsIncrementalDownload::FlushChunk()
 {
   NS_ASSERTION(mTotalSize != nsInt64(-1), "total size should be known");
 
@@ -323,25 +330,26 @@ nsIncrementalDownload::ReadCurrentSize()
     return rv;
 
   mCurrentSize = size; 
   return NS_OK;
 }
 
 // nsISupports
 
-NS_IMPL_ISUPPORTS8(nsIncrementalDownload,
+NS_IMPL_ISUPPORTS9(nsIncrementalDownload,
                    nsIIncrementalDownload,
                    nsIRequest,
                    nsIStreamListener,
                    nsIRequestObserver,
                    nsIObserver,
                    nsIInterfaceRequestor,
                    nsIChannelEventSink,
-                   nsISupportsWeakReference)
+                   nsISupportsWeakReference,
+                   nsIAsyncVerifyRedirectCallback)
 
 // nsIRequest
 
 NS_IMETHODIMP
 nsIncrementalDownload::GetName(nsACString &name)
 {
   NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
 
@@ -760,19 +768,20 @@ nsIncrementalDownload::ClearRequestHeade
   // to the actual size of the data. 
   return channel->SetRequestHeader(NS_LITERAL_CSTRING("Accept-Encoding"),
                                    NS_LITERAL_CSTRING(""), PR_FALSE);
 }
 
 // nsIChannelEventSink
 
 NS_IMETHODIMP
-nsIncrementalDownload::OnChannelRedirect(nsIChannel *oldChannel,
-                                         nsIChannel *newChannel,
-                                         PRUint32 flags)
+nsIncrementalDownload::AsyncOnChannelRedirect(nsIChannel *oldChannel,
+                                              nsIChannel *newChannel,
+                                              PRUint32 flags,
+                                              nsIAsyncVerifyRedirectCallback *cb)
 {
   // In response to a redirect, we need to propagate the Range header.  See bug
   // 311595.  Any failure code returned from this function aborts the redirect.
  
   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(oldChannel);
   NS_ENSURE_STATE(http);
 
   nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel);
@@ -787,26 +796,48 @@ nsIncrementalDownload::OnChannelRedirect
   // If we didn't have a Range header, then we must be doing a full download.
   nsCAutoString rangeVal;
   http->GetRequestHeader(rangeHdr, rangeVal);
   if (!rangeVal.IsEmpty()) {
     rv = newHttpChannel->SetRequestHeader(rangeHdr, rangeVal, PR_FALSE);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // Prepare to receive callback
+  mRedirectCallback = cb;
+  mNewRedirectChannel = newChannel;
+
   // Give the observer a chance to see this redirect notification.
   nsCOMPtr<nsIChannelEventSink> sink = do_GetInterface(mObserver);
-  if (sink)
-    rv = sink->OnChannelRedirect(oldChannel, newChannel, flags);
+  if (sink) {
+    rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
+    if (NS_FAILED(rv)) {
+        mRedirectCallback = nsnull;
+        mNewRedirectChannel = nsnull;
+    }
+    return rv;
+  }
+  (void) OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIncrementalDownload::OnRedirectVerifyCallback(nsresult result)
+{
+  NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
+  NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
 
   // Update mChannel, so we can Cancel the new channel.
-  if (NS_SUCCEEDED(rv))
-    mChannel = newChannel;
+  if (NS_SUCCEEDED(result))
+    mChannel = mNewRedirectChannel;
 
-  return rv;
+  mRedirectCallback->OnRedirectVerifyCallback(result);
+  mRedirectCallback = nsnull;
+  mNewRedirectChannel = nsnull;
+  return NS_OK;
 }
 
 extern nsresult
 net_NewIncrementalDownload(nsISupports *outer, const nsIID &iid, void **result)
 {
   if (outer)
     return NS_ERROR_NO_AGGREGATION;
 
--- a/netwerk/base/src/nsPACMan.cpp
+++ b/netwerk/base/src/nsPACMan.cpp
@@ -46,16 +46,17 @@
 #include "nsIHttpChannel.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsNetUtil.h"
 #include "nsAutoLock.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "prmon.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 //-----------------------------------------------------------------------------
 
 // Check to see if the underlying request was not an error page in the case of
 // a HTTP request.  For other types of channels, just return true.
 static PRBool
 HttpRequestSucceeded(nsIStreamLoader *loader)
 {
@@ -481,13 +482,19 @@ nsPACMan::GetInterface(const nsIID &iid,
     *result = static_cast<nsIChannelEventSink *>(this);
     return NS_OK;
   }
 
   return NS_ERROR_NO_INTERFACE;
 }
 
 NS_IMETHODIMP
-nsPACMan::OnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
-                            PRUint32 flags)
+nsPACMan::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
+                                 PRUint32 flags,
+                                 nsIAsyncVerifyRedirectCallback *callback)
 {
-  return newChannel->GetURI(getter_AddRefs(mPACURI));
+  nsresult rv = NS_OK;
+  if (NS_FAILED((rv = newChannel->GetURI(getter_AddRefs(mPACURI)))))
+      return rv;
+
+  callback->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
 }
--- a/netwerk/base/src/nsURIChecker.cpp
+++ b/netwerk/base/src/nsURIChecker.cpp
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsURIChecker.h"
 #include "nsIServiceManager.h"
 #include "nsIAuthPrompt.h"
 #include "nsIHttpChannel.h"
 #include "nsNetUtil.h"
 #include "nsString.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 //-----------------------------------------------------------------------------
 
 static PRBool
 ServerIsNES3x(nsIHttpChannel *httpChannel)
 {
     nsCAutoString server;
     httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
@@ -362,16 +363,18 @@ nsURIChecker::GetInterface(const nsIID &
     return QueryInterface(aIID, aResult);
 }
 
 //-----------------------------------------------------------------------------
 // nsIChannelEventSink methods:
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsURIChecker::OnChannelRedirect(nsIChannel *aOldChannel,
-                                nsIChannel *aNewChannel,
-                                PRUint32    aFlags)
+nsURIChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                     nsIChannel *aNewChannel,
+                                     PRUint32 aFlags,
+                                     nsIAsyncVerifyRedirectCallback *callback)
 {
     // We have a new channel
     mChannel = aNewChannel;
+    callback->OnRedirectVerifyCallback(NS_OK);
     return NS_OK;
 }
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -3039,16 +3039,17 @@ nsHttpChannel::ContinueProcessRedirectio
     }
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::ContinueProcessRedirection(nsresult rv)
 {
+    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);
 
@@ -4361,16 +4362,19 @@ nsHttpChannel::WaitForRedirectCallback()
 
     mWaitingForRedirectCallback = PR_TRUE;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::OnRedirectVerifyCallback(nsresult result)
 {
+    LOG(("nsHttpChannel::OnRedirectVerifyCallback [this=%p] "
+         "result=%x stack=%d mWaitingForRedirectCallback=%u\n",
+         this, result, mRedirectFuncStack.Length(), mWaitingForRedirectCallback));
     NS_ASSERTION(mWaitingForRedirectCallback,
                  "Someone forgot to call WaitForRedirectCallback() ?!");
     mWaitingForRedirectCallback = PR_FALSE;
 
     if (mCanceled && NS_SUCCEEDED(result))
         result = NS_BINDING_ABORTED;
 
     for (PRUint32 i = mRedirectFuncStack.Length(); i > 0;) {
--- a/netwerk/test/TestProtocols.cpp
+++ b/netwerk/test/TestProtocols.cpp
@@ -65,16 +65,17 @@
 #include "nsCRT.h"
 #include "nsIChannel.h"
 #include "nsIResumableChannel.h"
 #include "nsIURL.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIChannelEventSink.h" 
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIInterfaceRequestor.h" 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDNSService.h" 
 #include "nsIAuthPrompt.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPropertyBag2.h"
 #include "nsIWritablePropertyBag2.h"
@@ -234,22 +235,24 @@ TestChannelEventSink::TestChannelEventSi
 TestChannelEventSink::~TestChannelEventSink()
 {
 }
 
 
 NS_IMPL_ISUPPORTS1(TestChannelEventSink, nsIChannelEventSink)
 
 NS_IMETHODIMP
-TestChannelEventSink::OnChannelRedirect(nsIChannel *channel,
-                                        nsIChannel *newChannel,
-                                        PRUint32 flags)
+TestChannelEventSink::AsyncOnChannelRedirect(nsIChannel *channel,
+                                             nsIChannel *newChannel,
+                                             PRUint32 flags,
+                                             nsIAsyncVerifyRedirectCallback *callback)
 {
     LOG(("\n+++ TestChannelEventSink::OnChannelRedirect (with flags %x) +++\n",
          flags));
+    callback->OnRedirectVerifyCallback(NS_OK);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // TestAuthPrompt
 //-----------------------------------------------------------------------------
 
 class TestAuthPrompt : public nsIAuthPrompt
--- a/netwerk/test/unit/head_channels.js
+++ b/netwerk/test/unit/head_channels.js
@@ -144,14 +144,15 @@ ChannelEventSink.prototype = {
   },
 
   getInterface: function(iid) {
     if (iid.equals(Ci.nsIChannelEventSink))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
-  onChannelRedirect: function(oldChannel, newChannel, flags) {
-    if (this._flags & ES_ABORT_REDIRECT) {
+  asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
+    if (this._flags & ES_ABORT_REDIRECT)
       throw Cr.NS_BINDING_ABORTED;
-    }
+
+    callback.onRedirectVerifyCallback(Cr.NS_OK);
   }
 };
--- a/netwerk/test/unit/test_bug455311.js
+++ b/netwerk/test/unit/test_bug455311.js
@@ -35,17 +35,17 @@ NotificationCallbacks.prototype = {
       return this;
     }
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
   getInterface: function (iid)
   {
     return this.QueryInterface(iid);
   },
-  onChannelRedirect: function(oldChan, newChan, flags)
+  asyncOnChannelRedirect: function(oldChan, newChan, flags, callback)
   {
     do_check_eq(oldChan.URI.spec, this._origURI.spec);
     do_check_eq(oldChan.URI, this._origURI);
     do_check_eq(oldChan.originalURI.spec, this._origURI.spec);
     do_check_eq(oldChan.originalURI, this._origURI);
     do_check_eq(newChan.originalURI.spec, this._newURI.spec);
     do_check_eq(newChan.originalURI, newChan.URI);
     do_check_eq(newChan.URI.spec, this._newURI.spec);
--- a/netwerk/test/unit/test_event_sink.js
+++ b/netwerk/test/unit/test_event_sink.js
@@ -26,17 +26,17 @@ var eventsink = {
     if (outer)
       throw Components.results.NS_ERROR_NO_AGGREGATION;
     return this.QueryInterface(iid);
   },
   lockFactory: function eventsink_lockf(lock) {
     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
   },
 
-  onChannelRedirect: function eventsink_onredir(oldChan, newChan, flags) {
+  asyncOnChannelRedirect: function eventsink_onredir(oldChan, newChan, flags, callback) {
     // veto
     this.called = true;
     throw NS_BINDING_ABORTED;
   },
 
   getInterface: function eventsink_gi(iid) {
     if (iid.equals(Components.interfaces.nsIChannelEventSink))
       return this;
--- a/rdf/base/src/nsRDFXMLDataSource.cpp
+++ b/rdf/base/src/nsRDFXMLDataSource.cpp
@@ -120,16 +120,17 @@
 #include "rdf.h"
 #include "rdfutil.h"
 #include "prlog.h"
 #include "nsNameSpaceMap.h"
 #include "nsCRT.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsNetUtil.h"
 
 #include "rdfIDataSource.h"
 
 //----------------------------------------------------------------------
 
 static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
 static NS_DEFINE_CID(kRDFServiceCID,            NS_RDFSERVICE_CID);
@@ -896,42 +897,48 @@ RDFXMLDataSourceImpl::SetReadOnly(PRBool
 
 #include "nsITimelineService.h"
 
 // nsIChannelEventSink
 
 // This code is copied from nsSameOriginChecker::OnChannelRedirect. See
 // bug 475940 on providing this code in a shared location.
 NS_IMETHODIMP
-RDFXMLDataSourceImpl::OnChannelRedirect(nsIChannel *aOldChannel,
-                                        nsIChannel *aNewChannel,
-                                        PRUint32 aFlags)
+RDFXMLDataSourceImpl::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                             nsIChannel *aNewChannel,
+                                             PRUint32 aFlags,
+                                             nsIAsyncVerifyRedirectCallback *cb)
 {
-  NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
+    NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
 
-  nsresult rv;
-  nsCOMPtr<nsIScriptSecurityManager> secMan =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv;
+    nsCOMPtr<nsIScriptSecurityManager> secMan =
+        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIPrincipal> oldPrincipal;
+    secMan->GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
 
-  nsCOMPtr<nsIPrincipal> oldPrincipal;
-  secMan->GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
+    nsCOMPtr<nsIURI> newURI;
+    aNewChannel->GetURI(getter_AddRefs(newURI));
+    nsCOMPtr<nsIURI> newOriginalURI;
+    aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
 
-  nsCOMPtr<nsIURI> newURI;
-  aNewChannel->GetURI(getter_AddRefs(newURI));
-  nsCOMPtr<nsIURI> newOriginalURI;
-  aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
+    NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
 
-  NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
+    rv = oldPrincipal->CheckMayLoad(newURI, PR_FALSE);
+    if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
+        rv = oldPrincipal->CheckMayLoad(newOriginalURI, PR_FALSE);
+    }
 
-  rv = oldPrincipal->CheckMayLoad(newURI, PR_FALSE);
-  if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
-    rv = oldPrincipal->CheckMayLoad(newOriginalURI, PR_FALSE);
-  }
-  return rv;
+    if (NS_FAILED(rv))
+        return rv;
+
+    cb->OnRedirectVerifyCallback(NS_OK);
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 RDFXMLDataSourceImpl::Refresh(PRBool aBlocking)
 {
 #ifdef PR_LOGGING
     nsCAutoString spec;
     if (mURL) {
--- a/toolkit/components/places/src/AsyncFaviconHelpers.cpp
+++ b/toolkit/components/places/src/AsyncFaviconHelpers.cpp
@@ -44,16 +44,17 @@
 #include "nsIContentSniffer.h"
 #include "nsICacheService.h"
 #include "nsICacheVisitor.h"
 #include "nsICachingChannel.h"
 
 #include "nsNavHistory.h"
 #include "nsNavBookmarks.h"
 #include "nsFaviconService.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 #define TO_CHARBUFFER(_buffer) \
   reinterpret_cast<char*>(const_cast<PRUint8*>(_buffer))
 #define TO_INTBUFFER(_string) \
   reinterpret_cast<PRUint8*>(const_cast<char*>(_string.get()))
 
@@ -753,21 +754,23 @@ NS_IMETHODIMP
 FetchNetworkIconStep::GetInterface(const nsIID& uuid,
                                    void** aResult)
 {
   return QueryInterface(uuid, aResult);
 }
 
 
 NS_IMETHODIMP
-FetchNetworkIconStep::OnChannelRedirect(nsIChannel* oldChannel,
-                                        nsIChannel* newChannel,
-                                        PRUint32 flags)
+FetchNetworkIconStep::AsyncOnChannelRedirect(nsIChannel* oldChannel,
+                                             nsIChannel* newChannel,
+                                             PRUint32 flags,
+                                             nsIAsyncVerifyRedirectCallback *cb)
 {
   mChannel = newChannel;
+  cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// SetFaviconDataStep
 
 NS_IMPL_ISUPPORTS_INHERITED0(
--- a/toolkit/components/places/tests/mochitest/bug_411966/redirect.js
+++ b/toolkit/components/places/tests/mochitest/bug_411966/redirect.js
@@ -106,21 +106,22 @@ StreamListener.prototype = {
       this.mCallbackFunc(this.mData);
     else
       throw("Could not get page.");
 
     this.mChannel = null;
   },
 
   // nsIChannelEventSink
-  onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) {
+  asyncOnChannelRedirect: function (aOldChannel, aNewChannel, aFlags, callback) {
     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     ghist3.addDocumentRedirect(aOldChannel, aNewChannel, aFlags, true);
     // If redirecting, store the new channel
     this.mChannel = aNewChannel;
+    callback.onRedirectVerifyCallback(Components.results.NS_OK);
   },
 
   // nsIInterfaceRequestor
   getInterface: function (aIID) {
     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     try {
       return this.QueryInterface(aIID);
     } catch (e) {
--- a/toolkit/components/places/tests/network/test_history_redirects.js
+++ b/toolkit/components/places/tests/network/test_history_redirects.js
@@ -214,14 +214,15 @@ ChannelListener.prototype = {
     // The referrer is wrong since it's the first element in the redirects
     // chain, but this is good, since it will test a special path.
     ghist3.addURI(uri(FOUND_URL), false, true, uri(PERMA_REDIR_URL));
 
     continue_test();
   },
 
   // nsIChannelEventSink
-  onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) {
+  asyncOnChannelRedirect: function (aOldChannel, aNewChannel, aFlags, callback) {
     print("onChannelRedirect");
     this._got_onchannelredirect = true;
     ghist3.addDocumentRedirect(aOldChannel, aNewChannel, aFlags, true);
+    callback.onRedirectVerifyCallback(Components.results.NS_OK);
   },
 };
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -370,19 +370,20 @@ loadListener.prototype = {
     this._stream.setInputStream(aInputStream);
 
     // Get a byte array of the data
     this._bytes = this._bytes.concat(this._stream.readByteArray(aCount));
     this._countRead += aCount;
   },
 
   // nsIChannelEventSink
-  onChannelRedirect: function SRCH_loadCRedirect(aOldChannel, aNewChannel,
-                                                 aFlags) {
+  asyncOnChannelRedirect: function SRCH_loadCRedirect(aOldChannel, aNewChannel,
+                                                      aFlags, callback) {
     this._channel = aNewChannel;
+    callback.onRedirectVerifyCallback(Components.results.NS_OK);
   },
 
   // nsIInterfaceRequestor
   getInterface: function SRCH_load_GI(aIID) {
     return this.QueryInterface(aIID);
   },
 
   // nsIBadCertListener2
--- a/toolkit/mozapps/shared/CertUtils.jsm
+++ b/toolkit/mozapps/shared/CertUtils.jsm
@@ -138,26 +138,28 @@ function isBuiltinToken(tokenName) {
  * if the certificate is bad. See bug 304286.
  */
 function BadCertHandler(aAllowNonBuiltInCerts) {
   this.allowNonBuiltInCerts = aAllowNonBuiltInCerts;
 }
 BadCertHandler.prototype = {
 
   // nsIChannelEventSink
-  onChannelRedirect: function(oldChannel, newChannel, flags) {
+  asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
     if (this.allowNonBuiltInCerts)
       return;
 
     // make sure the certificate of the old channel checks out before we follow
     // a redirect from it.  See bug 340198.
     // Don't call checkCert for internal redirects. See bug 569648.
     if (!(flags & Ci.nsIChannelEventSink.REDIRECT_INTERNAL))
       checkCert(oldChannel);
-  },
+    
+    callback.onRedirectVerifyCallback(Components.results.NS_OK);
+    },
 
   // Suppress any certificate errors
   notifyCertProblem: function(socketInfo, status, targetSite) {
     return true;
   },
 
   // Suppress any ssl errors
   notifySSLError: function(socketInfo, error, targetSite) {
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -59,16 +59,17 @@
 #include "nsIScriptSecurityManager.h"
 
 #include "nsITransport.h"
 #include "nsISocketTransport.h"
 
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
 
 #if defined(PR_LOGGING)
 //
 // Log module for nsIDocumentLoader logging...
 //
 // To enable logging (see prlog.h for full details):
@@ -1579,19 +1580,20 @@ CalcMaxProgressCallback(PLDHashTable *ta
 
 PRInt64 nsDocLoader::CalculateMaxProgress()
 {
   nsInt64 max = mCompletedTotalProgress;
   PL_DHashTableEnumerate(&mRequestInfoHash, CalcMaxProgressCallback, &max);
   return max;
 }
 
-NS_IMETHODIMP nsDocLoader::OnChannelRedirect(nsIChannel *aOldChannel,
-                                             nsIChannel *aNewChannel,
-                                             PRUint32    aFlags)
+NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                                  nsIChannel *aNewChannel,
+                                                  PRUint32 aFlags,
+                                                  nsIAsyncVerifyRedirectCallback *cb)
 {
   if (aOldChannel)
   {
     nsLoadFlags loadFlags = 0;
     PRInt32 stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
                          nsIWebProgressListener::STATE_IS_REQUEST;
 
     aOldChannel->GetLoadFlags(&loadFlags);
@@ -1606,16 +1608,17 @@ NS_IMETHODIMP nsDocLoader::OnChannelRedi
       NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
 #endif /* DEBUG */
     }
 
     OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
     FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
   }
 
+  cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 /*
  * Implementation of nsISecurityEventSink method...
  */
 
 NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -65,16 +65,17 @@
 #include "nsIPrefService.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "prlog.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull;
 
 static const PRUint32 kRescheduleLimit = 3;
 
 #if defined(PR_LOGGING)
 //
 // To enable logging (see prlog.h for full details):
@@ -278,23 +279,26 @@ nsManifestCheck::GetInterface(const nsII
     return NS_ERROR_NO_INTERFACE;
 }
 
 //-----------------------------------------------------------------------------
 // nsManifestCheck::nsIChannelEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsManifestCheck::OnChannelRedirect(nsIChannel *aOldChannel,
-                                   nsIChannel *aNewChannel,
-                                   PRUint32 aFlags)
+nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                        nsIChannel *aNewChannel,
+                                        PRUint32 aFlags,
+                                        nsIAsyncVerifyRedirectCallback *callback)
 {
     // Redirects should cause the load (and therefore the update) to fail.
-    if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)
+    if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
+        callback->OnRedirectVerifyCallback(NS_OK);
         return NS_OK;
+    }
     aOldChannel->Cancel(NS_ERROR_ABORT);
     return NS_ERROR_ABORT;
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateItem::nsISupports
 //-----------------------------------------------------------------------------
 
@@ -486,19 +490,20 @@ nsOfflineCacheUpdateItem::GetInterface(c
     return NS_ERROR_NO_INTERFACE;
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateItem::nsIChannelEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsOfflineCacheUpdateItem::OnChannelRedirect(nsIChannel *aOldChannel,
-                                            nsIChannel *aNewChannel,
-                                            PRUint32 aFlags)
+nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                                 nsIChannel *aNewChannel,
+                                                 PRUint32 aFlags,
+                                                 nsIAsyncVerifyRedirectCallback *cb)
 {
     if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
         // Don't allow redirect in case of non-internal redirect and cancel
         // the channel to clean the cache entry.
         aOldChannel->Cancel(NS_ERROR_ABORT);
         return NS_ERROR_ABORT;
     }
 
@@ -534,16 +539,17 @@ nsOfflineCacheUpdateItem::OnChannelRedir
     NS_ENSURE_STATE(httpChannel);
 
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
                                   NS_LITERAL_CSTRING("offline-resource"),
                                   PR_FALSE);
 
     mChannel = aNewChannel;
 
+    cb->OnRedirectVerifyCallback(NS_OK);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateItem::nsIDOMLoadStatus
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -55,16 +55,17 @@
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
 #include "nsAutoPtr.h"
 #include "prtime.h"
 #include "prlog.h"
 #include "plstr.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 #if defined(PR_LOGGING)
 //
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=nsPrefetch:5
 //    set NSPR_LOG_FILE=prefetch.log
 //
@@ -358,19 +359,20 @@ nsPrefetchNode::GetInterface(const nsIID
     return NS_ERROR_NO_INTERFACE;
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchNode::nsIChannelEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsPrefetchNode::OnChannelRedirect(nsIChannel *aOldChannel,
-                                  nsIChannel *aNewChannel,
-                                  PRUint32 aFlags)
+nsPrefetchNode::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                       nsIChannel *aNewChannel,
+                                       PRUint32 aFlags,
+                                       nsIAsyncVerifyRedirectCallback *callback)
 {
     nsCOMPtr<nsIURI> newURI;
     nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsICachingChannel> oldCachingChannel =
         do_QueryInterface(aOldChannel);
@@ -390,16 +392,17 @@ nsPrefetchNode::OnChannelRedirect(nsICha
     NS_ENSURE_STATE(httpChannel);
 
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
                                   NS_LITERAL_CSTRING("prefetch"),
                                   PR_FALSE);
 
     mChannel = aNewChannel;
 
+    callback->OnRedirectVerifyCallback(NS_OK);
     return NS_OK;
 }
 
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService <public>
 //-----------------------------------------------------------------------------
 
--- a/xpinstall/src/nsXPInstallManager.cpp
+++ b/xpinstall/src/nsXPInstallManager.cpp
@@ -86,16 +86,17 @@
 #include "nsIX509Cert3.h"
 
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 
 #include "CertReader.h"
 
 #include "nsEmbedCID.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
 
 #define PREF_XPINSTALL_ENABLED                "xpinstall.enabled"
 #define PREF_XPINSTALL_CONFIRM_DLG            "xpinstall.dialog.confirm"
 #define PREF_XPINSTALL_STATUS_DLG_SKIN        "xpinstall.dialog.progress.skin"
 #define PREF_XPINSTALL_STATUS_DLG_CHROME      "xpinstall.dialog.progress.chrome"
 #define PREF_XPINSTALL_STATUS_DLG_TYPE_SKIN   "xpinstall.dialog.progress.type.skin"
 #define PREF_XPINSTALL_STATUS_DLG_TYPE_CHROME "xpinstall.dialog.progress.type.chrome"
 
@@ -1318,21 +1319,29 @@ nsXPInstallManager::GetInterface(const n
         if (!mFromChrome)
             return NS_ERROR_NO_INTERFACE;
     }
     return QueryInterface(eventSinkIID, (void**)_retval);
 }
 
 // nsIChannelEventSink method
 NS_IMETHODIMP
-nsXPInstallManager::OnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, PRUint32 flags)
+nsXPInstallManager::AsyncOnChannelRedirect(nsIChannel *oldChannel,
+                                           nsIChannel *newChannel,
+                                           PRUint32 flags,
+                                           nsIAsyncVerifyRedirectCallback *callback)
 {
     // Chrome triggered installs need to have their certificates checked
-    if (mFromChrome)
-        return CheckCert(oldChannel);
+    if (mFromChrome) {
+        nsresult rv = CheckCert(oldChannel);
+        if (NS_FAILED(rv()))
+            return rv;
+    }
+
+    callback->OnRedirectVerifyCallback(NS_OK);
     return NS_OK;
 }
 
 // nsIBadCertListener2 methods
 NS_IMETHODIMP
 nsXPInstallManager::NotifyCertProblem(nsIInterfaceRequestor *socketInfo,
                                       nsISSLStatus *status,
                                       const nsACString &targetSite,