diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 2f3441aa6ee..696dec4420d 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -95,6 +95,7 @@ #include "nsIChromeRegistry.h" #include "nsPrintfCString.h" #include "nsIContentSecurityPolicy.h" +#include "nsIAsyncVerifyRedirectCallback.h" static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID); @@ -3257,9 +3258,10 @@ nsScriptSecurityManager::CheckXPCPermissions(JSContext* cx, // Method implementing nsIChannelEventSink // ///////////////////////////////////////////// NS_IMETHODIMP -nsScriptSecurityManager::OnChannelRedirect(nsIChannel* oldChannel, - nsIChannel* newChannel, - PRUint32 redirFlags) +nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel, + nsIChannel* newChannel, + PRUint32 redirFlags, + nsIAsyncVerifyRedirectCallback *cb) { nsCOMPtr oldPrincipal; GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal)); @@ -3278,7 +3280,12 @@ nsScriptSecurityManager::OnChannelRedirect(nsIChannel* oldChannel, 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; } diff --git a/content/base/src/nsCSPService.cpp b/content/base/src/nsCSPService.cpp index 3e2ee9b3053..ff825fe3ac2 100644 --- a/content/base/src/nsCSPService.cpp +++ b/content/base/src/nsCSPService.cpp @@ -53,6 +53,8 @@ #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; @@ -203,10 +205,13 @@ CSPService::ShouldProcess(PRUint32 aContentType, /* 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 policyContainer; nsCOMPtr props(do_QueryInterface(oldChannel)); @@ -257,19 +262,22 @@ CSPService::OnChannelRedirect(nsIChannel *oldChannel, 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 diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 9554a7568f6..f7a18d465ce 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -162,6 +162,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #include "nsIUGenCategory.h" #include "nsIDragService.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIInterfaceRequestor.h" #include "nsIOfflineCacheUpdate.h" #include "nsCPrefetchService.h" @@ -5149,14 +5150,14 @@ NS_IMPL_ISUPPORTS2(nsSameOriginChecker, 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 oldPrincipal; nsContentUtils::GetSecurityManager()-> @@ -5173,7 +5174,12 @@ nsSameOriginChecker::OnChannelRedirect(nsIChannel *aOldChannel, 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 diff --git a/content/base/src/nsCrossSiteListenerProxy.cpp b/content/base/src/nsCrossSiteListenerProxy.cpp index 9f0fe96c57f..7a865bff2b1 100644 --- a/content/base/src/nsCrossSiteListenerProxy.cpp +++ b/content/base/src/nsCrossSiteListenerProxy.cpp @@ -52,8 +52,10 @@ #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; @@ -81,9 +83,9 @@ private: nsIChannel* mChannel; }; -NS_IMPL_ISUPPORTS4(nsCrossSiteListenerProxy, nsIStreamListener, +NS_IMPL_ISUPPORTS5(nsCrossSiteListenerProxy, nsIStreamListener, nsIRequestObserver, nsIChannelEventSink, - nsIInterfaceRequestor) + nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback) /* static */ void @@ -370,11 +372,11 @@ nsCrossSiteListenerProxy::GetInterface(const nsIID & aIID, void **aResult) } 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); @@ -387,22 +389,57 @@ nsCrossSiteListenerProxy::OnChannelRedirect(nsIChannel *aOldChannel, 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 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; +} - canceller.DontCancel(); - +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"); + + 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; } diff --git a/content/base/src/nsCrossSiteListenerProxy.h b/content/base/src/nsCrossSiteListenerProxy.h index 0bcdf42dce3..d190f38815c 100644 --- a/content/base/src/nsCrossSiteListenerProxy.h +++ b/content/base/src/nsCrossSiteListenerProxy.h @@ -46,6 +46,7 @@ #include "nsTArray.h" #include "nsIInterfaceRequestor.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" class nsIURI; class nsIParser; @@ -56,7 +57,8 @@ IsValidHTTPToken(const nsCSubstring& aToken); class nsCrossSiteListenerProxy : public nsIStreamListener, public nsIInterfaceRequestor, - public nsIChannelEventSink + public nsIChannelEventSink, + public nsIAsyncVerifyRedirectCallback { public: nsCrossSiteListenerProxy(nsIStreamListener* aOuter, @@ -77,6 +79,7 @@ public: NS_DECL_NSISTREAMLISTENER NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSICHANNELEVENTSINK + NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK // Must be called at startup. static void Startup(); @@ -100,6 +103,9 @@ private: nsCString mPreflightMethod; nsTArray mPreflightHeaders; nsTArray mAllowedHTTPErrors; + nsCOMPtr mRedirectCallback; + nsCOMPtr mOldRedirectChannel; + nsCOMPtr mNewRedirectChannel; }; #endif diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 477824f2979..4692b11fd41 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -69,6 +69,7 @@ #include "nsIWebNavigationInfo.h" #include "nsIScriptChannel.h" #include "nsIBlocklistService.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsPluginError.h" @@ -1025,9 +1026,10 @@ nsObjectLoadingContent::GetInterface(const nsIID & aIID, void **aResult) // 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) { @@ -1035,6 +1037,7 @@ nsObjectLoadingContent::OnChannelRedirect(nsIChannel *aOldChannel, } mChannel = aNewChannel; + cb->OnRedirectVerifyCallback(NS_OK); return NS_OK; } diff --git a/content/base/src/nsSyncLoadService.cpp b/content/base/src/nsSyncLoadService.cpp index 7a147037590..5f372d9a752 100644 --- a/content/base/src/nsSyncLoadService.cpp +++ b/content/base/src/nsSyncLoadService.cpp @@ -45,6 +45,7 @@ #include "nsIChannel.h" #include "nsIDOMLoadListener.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIInterfaceRequestor.h" #include "nsString.h" #include "nsWeakReference.h" @@ -360,14 +361,16 @@ nsSyncLoader::Error(nsIDOMEvent* aEvent) } 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; } diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index a5e0dd5806b..8b7a3dfb231 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -99,6 +99,7 @@ #include "nsIChannelPolicy.h" #include "nsChannelPolicy.h" #include "nsIContentSecurityPolicy.h" +#include "nsAsyncRedirectVerifyHelper.h" #define LOAD_STR "load" #define ERROR_STR "error" @@ -486,13 +487,17 @@ nsACProxyListener::OnDataAvailable(nsIRequest *aRequest, } 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 @@ -2988,13 +2993,60 @@ nsXMLHttpRequest::ChangeState(PRUint32 aState, PRBool aBroadcast) 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 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?"); @@ -3002,7 +3054,11 @@ nsXMLHttpRequest::OnChannelRedirect(nsIChannel *aOldChannel, 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 @@ -3012,20 +3068,44 @@ nsXMLHttpRequest::OnChannelRedirect(nsIChannel *aOldChannel, } } + // Prepare to receive callback + mRedirectCallback = callback; + mNewRedirectChannel = aNewChannel; + if (mChannelEventSink) { - rv = - mChannelEventSink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags); + nsRefPtr 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: // diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index 89ffc2dca6a..cfa74216498 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -51,6 +51,7 @@ #include "jsapi.h" #include "nsIScriptContext.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIInterfaceRequestor.h" #include "nsIHttpHeaderVisitor.h" #include "nsIProgressEventSink.h" @@ -70,6 +71,7 @@ #include "nsDOMEventTargetWrapperCache.h" class nsILoadGroup; +class AsyncVerifyRedirectCallbackForwarder; class nsAccessControlLRUCache { @@ -340,6 +342,9 @@ protected: void StartProgressEventTimer(); + friend class AsyncVerifyRedirectCallbackForwarder; + void OnRedirectVerifyCallback(nsresult result); + nsCOMPtr mContext; nsCOMPtr mPrincipal; nsCOMPtr mChannel; @@ -413,6 +418,9 @@ protected: nsCOMPtr mProgressNotifier; PRPackedBool mFirstStartRequestSeen; + + nsCOMPtr mRedirectCallback; + nsCOMPtr mNewRedirectChannel; }; // helper class to expose a progress DOM Event diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index ee60ff0b175..8898351698f 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -86,6 +86,7 @@ #include "BasicLayers.h" #include #include "nsIDocShellTreeItem.h" +#include "nsIAsyncVerifyRedirectCallback.h" #ifdef MOZ_OGG #include "nsOggDecoder.h" @@ -352,15 +353,19 @@ NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest* 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 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; } diff --git a/content/media/nsMediaStream.cpp b/content/media/nsMediaStream.cpp index 1ced33d8c61..c3d20616561 100644 --- a/content/media/nsMediaStream.cpp +++ b/content/media/nsMediaStream.cpp @@ -56,6 +56,7 @@ #include "nsDOMError.h" #include "nsICachingChannel.h" #include "nsURILoader.h" +#include "nsIAsyncVerifyRedirectCallback.h" #define HTTP_OK_CODE 200 #define HTTP_PARTIAL_RESPONSE_CODE 206 @@ -126,13 +127,20 @@ nsMediaChannelStream::Listener::OnDataAvailable(nsIRequest* aRequest, } 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 diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 80bb98aee83..b72bfa4ba5e 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -85,6 +85,7 @@ #include "nsIAuthPrompt2.h" #include "nsTextFormatter.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIUploadChannel.h" #include "nsISecurityEventSink.h" #include "mozilla/FunctionTimer.h" @@ -502,8 +503,9 @@ nsPingListener::GetInterface(const nsIID &iid, void **result) } NS_IMETHODIMP -nsPingListener::OnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan, - PRUint32 flags) +nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan, + PRUint32 flags, + nsIAsyncVerifyRedirectCallback *callback) { nsCOMPtr newURI; newChan->GetURI(getter_AddRefs(newURI)); @@ -511,8 +513,10 @@ nsPingListener::OnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan, if (!CheckPingURI(newURI, mContent)) return NS_ERROR_ABORT; - if (!mRequireSameHost) + if (!mRequireSameHost) { + callback->OnRedirectVerifyCallback(NS_OK); return NS_OK; + } // XXXbz should this be using something more like the nsContentUtils // same-origin checker? @@ -523,6 +527,7 @@ nsPingListener::OnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan, if (!IsSameHost(oldURI, newURI)) return NS_ERROR_ABORT; + callback->OnRedirectVerifyCallback(NS_OK); return NS_OK; } diff --git a/modules/libpr0n/src/imgLoader.cpp b/modules/libpr0n/src/imgLoader.cpp index c1034b666b1..dc5d39acb75 100644 --- a/modules/libpr0n/src/imgLoader.cpp +++ b/modules/libpr0n/src/imgLoader.cpp @@ -52,6 +52,7 @@ #include "nsIPrefService.h" #include "nsIProgressEventSink.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIProxyObjectManager.h" #include "nsIServiceManager.h" #include "nsIFileURL.h" @@ -319,9 +320,10 @@ nsProgressNotificationProxy::OnStatus(nsIRequest* request, } 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!"); @@ -337,9 +339,13 @@ nsProgressNotificationProxy::OnChannelRedirect(nsIChannel *oldChannel, 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 diff --git a/modules/libpr0n/src/imgRequest.cpp b/modules/libpr0n/src/imgRequest.cpp index 9a596023456..aabac9bd50d 100644 --- a/modules/libpr0n/src/imgRequest.cpp +++ b/modules/libpr0n/src/imgRequest.cpp @@ -83,6 +83,7 @@ #include "nsIPrefBranch2.h" #include "imgDiscardTracker.h" +#include "nsAsyncRedirectVerifyHelper.h" #define DISCARD_PREF "image.mem.discardable" #define DECODEONDRAW_PREF "image.mem.decodeondraw" @@ -158,12 +159,13 @@ imgRequestPrefObserver::Observe(nsISupports *aSubject, 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"), @@ -1119,24 +1121,49 @@ imgRequest::GetInterface(const nsIID & aIID, void **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 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 @@ -1149,17 +1176,23 @@ imgRequest::OnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, PR // make sure we have a protocol that returns data rather than opens // an external application, e.g. mailto: nsCOMPtr 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 newURI; - newChannel->GetOriginalURI(getter_AddRefs(newURI)); + mChannel->GetOriginalURI(getter_AddRefs(newURI)); nsCAutoString newspec; if (newURI) newURI->GetSpec(newspec); @@ -1186,5 +1219,7 @@ imgRequest::OnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, PR } } - return rv; + mRedirectCallback->OnRedirectVerifyCallback(NS_OK); + mRedirectCallback = nsnull; + return NS_OK; } diff --git a/modules/libpr0n/src/imgRequest.h b/modules/libpr0n/src/imgRequest.h index 2103ac45d29..2e2ac89dd5c 100644 --- a/modules/libpr0n/src/imgRequest.h +++ b/modules/libpr0n/src/imgRequest.h @@ -62,6 +62,7 @@ #include "ImageErrors.h" #include "imgIRequest.h" #include "imgContainer.h" +#include "nsIAsyncVerifyRedirectCallback.h" class imgCacheValidator; @@ -74,7 +75,8 @@ class imgRequest : public imgIDecoderObserver, public nsIStreamListener, public nsSupportsWeakReference, public nsIChannelEventSink, - public nsIInterfaceRequestor + public nsIInterfaceRequestor, + public nsIAsyncVerifyRedirectCallback { public: imgRequest(); @@ -182,6 +184,7 @@ public: NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK private: friend class imgMemoryReporter; @@ -211,7 +214,8 @@ private: imgCacheValidator *mValidator; nsCategoryCache mImageSniffers; - + nsCOMPtr mRedirectCallback; + nsCOMPtr 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; diff --git a/netwerk/base/public/nsAsyncRedirectVerifyHelper.h b/netwerk/base/public/nsAsyncRedirectVerifyHelper.h index 6c44b4ce3c1..f922b18e8e5 100644 --- a/netwerk/base/public/nsAsyncRedirectVerifyHelper.h +++ b/netwerk/base/public/nsAsyncRedirectVerifyHelper.h @@ -1,3 +1,4 @@ +/* -*- 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 * @@ -20,6 +21,7 @@ * * Contributor(s): * Honza Bambas + * Bjarne Geir Herland * * 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 @@ -39,8 +41,13 @@ #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; @@ -49,14 +56,27 @@ class nsIChannel; * 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. * @@ -81,8 +101,57 @@ protected: nsCOMPtr mNewChan; PRUint32 mFlags; PRBool mWaitingForRedirectCallback; + nsCOMPtr 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 diff --git a/netwerk/base/public/nsIChannelEventSink.idl b/netwerk/base/public/nsIChannelEventSink.idl index d8c0aa16743..14a4dc7310e 100644 --- a/netwerk/base/public/nsIChannelEventSink.idl +++ b/netwerk/base/public/nsIChannelEventSink.idl @@ -24,6 +24,7 @@ * Gagan Saksena (original author) * Darin Fisher * Christian Biesinger + * Bjarne Geir Herland * * 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 @@ -42,6 +43,7 @@ #include "nsISupports.idl" interface nsIChannel; +interface nsIAsyncVerifyRedirectCallback; /** * Implement this interface to receive control over various channel events. @@ -49,10 +51,9 @@ interface nsIChannel; * 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 { /** @@ -82,7 +83,30 @@ interface nsIChannelEventSink : nsISupports /** * 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. @@ -93,11 +117,14 @@ interface nsIChannelEventSink : nsISupports * 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 Throwing an exception will cancel the load. No network - * request for the new channel will be made. + * @throw 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); }; diff --git a/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp b/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp index 41268e29a68..b4657887274 100644 --- a/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp +++ b/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp @@ -1,3 +1,4 @@ +/* -*- 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 * @@ -20,6 +21,7 @@ * * Contributor(s): * Honza Bambas + * Bjarne Geir Herland * * 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 @@ -35,6 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "prlog.h" #include "nsAsyncRedirectVerifyHelper.h" #include "nsThreadUtils.h" #include "nsNetUtil.h" @@ -44,15 +47,60 @@ #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 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; @@ -73,55 +121,172 @@ nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan, return NS_OK; } -void -nsAsyncRedirectVerifyHelper::Callback(nsresult result) +NS_IMETHODIMP +nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result) { - // TODO E10S OnRedirectCallback has to be called on the original process - nsCOMPtr callback(do_QueryInterface(mOldChan)); - NS_ASSERTION(callback, "nsAsyncRedirectVerifyHelper: oldChannel doesn't" - " implement nsIAsyncVerifyRedirectCallback"); + LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() " + "result=%x expectedCBs=%u mResult=%x", + result, mExpectedCallbacks, mResult)); - if (callback) - callback->OnRedirectVerifyCallback(result); + --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) +{ + 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 + callback(do_QueryInterface(mOldChan)); + + 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; + + // Now, dispatch the callback on the event-target which called Init() + nsRefPtr 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 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 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 oldChannelInternal = + do_QueryInterface(mOldChan); + if (oldChannelInternal) { + oldChannelInternal->GetCanceled(&canceled); + if (canceled) + return true; + } + + return false; +} \ No newline at end of file diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp index 799de3b9785..2683b283cec 100644 --- a/netwerk/base/src/nsIOService.cpp +++ b/netwerk/base/src/nsIOService.cpp @@ -324,13 +324,15 @@ NS_IMPL_THREADSAFE_ISUPPORTS5(nsIOService, //////////////////////////////////////////////////////////////////////////////// nsresult -nsIOService::OnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, - PRUint32 flags) +nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, + PRUint32 flags, + nsAsyncRedirectVerifyHelper *helper) { nsCOMPtr 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; } @@ -340,11 +342,11 @@ nsIOService::OnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, 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; } diff --git a/netwerk/base/src/nsIOService.h b/netwerk/base/src/nsIOService.h index e1143bd09e4..e1a47f9d6bd 100644 --- a/netwerk/base/src/nsIOService.h +++ b/netwerk/base/src/nsIOService.h @@ -57,6 +57,7 @@ #include "nsIContentSniffer.h" #include "nsCategoryCache.h" #include "nsINetworkLinkService.h" +#include "nsAsyncRedirectVerifyHelper.h" #define NS_N(x) (sizeof(x)/sizeof(*x)) @@ -95,8 +96,9 @@ public: // 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& GetContentSniffers() { diff --git a/netwerk/base/src/nsIncrementalDownload.cpp b/netwerk/base/src/nsIncrementalDownload.cpp index 75b7ee2df3c..a68ed018415 100644 --- a/netwerk/base/src/nsIncrementalDownload.cpp +++ b/netwerk/base/src/nsIncrementalDownload.cpp @@ -40,6 +40,7 @@ #include "nsIRequestObserver.h" #include "nsIProgressEventSink.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIInterfaceRequestor.h" #include "nsIObserverService.h" #include "nsIObserver.h" @@ -126,6 +127,7 @@ class nsIncrementalDownload : public nsIIncrementalDownload , public nsIInterfaceRequestor , public nsIChannelEventSink , public nsSupportsWeakReference + , public nsIAsyncVerifyRedirectCallback { public: NS_DECL_ISUPPORTS @@ -136,6 +138,7 @@ public: NS_DECL_NSIOBSERVER NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSICHANNELEVENTSINK + NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK nsIncrementalDownload(); @@ -150,26 +153,28 @@ private: nsresult ReadCurrentSize(); nsresult ClearRequestHeader(nsIHttpChannel *channel); - nsCOMPtr mObserver; - nsCOMPtr mObserverContext; - nsCOMPtr mProgressSink; - nsCOMPtr mURI; - nsCOMPtr mFinalURI; - nsCOMPtr mDest; - nsCOMPtr mChannel; - nsCOMPtr mTimer; - nsAutoArrayPtr mChunk; - PRInt32 mChunkLen; - PRInt32 mChunkSize; - PRInt32 mInterval; - nsInt64 mTotalSize; - nsInt64 mCurrentSize; - PRUint32 mLoadFlags; - PRInt32 mNonPartialCount; - nsresult mStatus; - PRPackedBool mIsPending; - PRPackedBool mDidOnStartRequest; - PRTime mLastProgressUpdate; + nsCOMPtr mObserver; + nsCOMPtr mObserverContext; + nsCOMPtr mProgressSink; + nsCOMPtr mURI; + nsCOMPtr mFinalURI; + nsCOMPtr mDest; + nsCOMPtr mChannel; + nsCOMPtr mTimer; + nsAutoArrayPtr mChunk; + PRInt32 mChunkLen; + PRInt32 mChunkSize; + PRInt32 mInterval; + nsInt64 mTotalSize; + nsInt64 mCurrentSize; + PRUint32 mLoadFlags; + PRInt32 mNonPartialCount; + nsresult mStatus; + PRPackedBool mIsPending; + PRPackedBool mDidOnStartRequest; + PRTime mLastProgressUpdate; + nsCOMPtr mRedirectCallback; + nsCOMPtr mNewRedirectChannel; }; nsIncrementalDownload::nsIncrementalDownload() @@ -184,6 +189,8 @@ nsIncrementalDownload::nsIncrementalDownload() , mIsPending(PR_FALSE) , mDidOnStartRequest(PR_FALSE) , mLastProgressUpdate(0) + , mRedirectCallback(nsnull) + , mNewRedirectChannel(nsnull) { } @@ -328,7 +335,7 @@ nsIncrementalDownload::ReadCurrentSize() // nsISupports -NS_IMPL_ISUPPORTS8(nsIncrementalDownload, +NS_IMPL_ISUPPORTS9(nsIncrementalDownload, nsIIncrementalDownload, nsIRequest, nsIStreamListener, @@ -336,7 +343,8 @@ NS_IMPL_ISUPPORTS8(nsIncrementalDownload, nsIObserver, nsIInterfaceRequestor, nsIChannelEventSink, - nsISupportsWeakReference) + nsISupportsWeakReference, + nsIAsyncVerifyRedirectCallback) // nsIRequest @@ -765,9 +773,10 @@ nsIncrementalDownload::ClearRequestHeader(nsIHttpChannel *channel) // 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. @@ -792,16 +801,38 @@ nsIncrementalDownload::OnChannelRedirect(nsIChannel *oldChannel, NS_ENSURE_SUCCESS(rv, rv); } + // Prepare to receive callback + mRedirectCallback = cb; + mNewRedirectChannel = newChannel; + // Give the observer a chance to see this redirect notification. nsCOMPtr 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 diff --git a/netwerk/base/src/nsPACMan.cpp b/netwerk/base/src/nsPACMan.cpp index 4983ab57c12..94b4a44b924 100644 --- a/netwerk/base/src/nsPACMan.cpp +++ b/netwerk/base/src/nsPACMan.cpp @@ -51,6 +51,7 @@ #include "nsAutoPtr.h" #include "nsCRT.h" #include "prmon.h" +#include "nsIAsyncVerifyRedirectCallback.h" //----------------------------------------------------------------------------- @@ -486,8 +487,14 @@ nsPACMan::GetInterface(const nsIID &iid, void **result) } 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; } diff --git a/netwerk/base/src/nsURIChecker.cpp b/netwerk/base/src/nsURIChecker.cpp index 9a65cb0a3ea..a85969c3c68 100644 --- a/netwerk/base/src/nsURIChecker.cpp +++ b/netwerk/base/src/nsURIChecker.cpp @@ -43,6 +43,7 @@ #include "nsIHttpChannel.h" #include "nsNetUtil.h" #include "nsString.h" +#include "nsIAsyncVerifyRedirectCallback.h" //----------------------------------------------------------------------------- @@ -367,11 +368,13 @@ nsURIChecker::GetInterface(const nsIID & aIID, void **aResult) //----------------------------------------------------------------------------- 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; } diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 047b64528d9..078e25fb711 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -3044,6 +3044,7 @@ nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) nsresult nsHttpChannel::ContinueProcessRedirection(nsresult rv) { + LOG(("ContinueProcessRedirection [rv=%x]\n", rv)); if (NS_FAILED(rv)) return rv; @@ -4366,6 +4367,9 @@ nsHttpChannel::WaitForRedirectCallback() 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; diff --git a/netwerk/test/TestProtocols.cpp b/netwerk/test/TestProtocols.cpp index f3ce7a2389a..e23d8fc3046 100644 --- a/netwerk/test/TestProtocols.cpp +++ b/netwerk/test/TestProtocols.cpp @@ -70,6 +70,7 @@ #include "nsIHttpChannelInternal.h" #include "nsIHttpHeaderVisitor.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIDNSService.h" @@ -239,12 +240,14 @@ 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; } diff --git a/netwerk/test/unit/head_channels.js b/netwerk/test/unit/head_channels.js index 75ea0563698..0261cba64de 100644 --- a/netwerk/test/unit/head_channels.js +++ b/netwerk/test/unit/head_channels.js @@ -149,9 +149,10 @@ ChannelEventSink.prototype = { 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); } }; diff --git a/netwerk/test/unit/test_bug455311.js b/netwerk/test/unit/test_bug455311.js index 47c07180bba..71baabec1b8 100644 --- a/netwerk/test/unit/test_bug455311.js +++ b/netwerk/test/unit/test_bug455311.js @@ -40,7 +40,7 @@ NotificationCallbacks.prototype = { { 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); diff --git a/netwerk/test/unit/test_event_sink.js b/netwerk/test/unit/test_event_sink.js index 35a31b96953..34a64ee1664 100644 --- a/netwerk/test/unit/test_event_sink.js +++ b/netwerk/test/unit/test_event_sink.js @@ -31,7 +31,7 @@ var eventsink = { 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; diff --git a/rdf/base/src/nsRDFXMLDataSource.cpp b/rdf/base/src/nsRDFXMLDataSource.cpp index 974d838213a..6c9760996b9 100644 --- a/rdf/base/src/nsRDFXMLDataSource.cpp +++ b/rdf/base/src/nsRDFXMLDataSource.cpp @@ -125,6 +125,7 @@ #include "nsCycleCollectionParticipant.h" #include "nsIScriptSecurityManager.h" #include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsNetUtil.h" #include "rdfIDataSource.h" @@ -901,32 +902,38 @@ RDFXMLDataSourceImpl::SetReadOnly(PRBool aIsReadOnly) // 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 secMan = - do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv; + nsCOMPtr secMan = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr oldPrincipal; - secMan->GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal)); + nsCOMPtr oldPrincipal; + secMan->GetChannelPrincipal(aOldChannel, getter_AddRefs(oldPrincipal)); - nsCOMPtr newURI; - aNewChannel->GetURI(getter_AddRefs(newURI)); - nsCOMPtr newOriginalURI; - aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI)); + nsCOMPtr newURI; + aNewChannel->GetURI(getter_AddRefs(newURI)); + nsCOMPtr 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); - } - return rv; + rv = oldPrincipal->CheckMayLoad(newURI, PR_FALSE); + if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) { + rv = oldPrincipal->CheckMayLoad(newOriginalURI, PR_FALSE); + } + + if (NS_FAILED(rv)) + return rv; + + cb->OnRedirectVerifyCallback(NS_OK); + return NS_OK; } NS_IMETHODIMP diff --git a/toolkit/components/places/src/AsyncFaviconHelpers.cpp b/toolkit/components/places/src/AsyncFaviconHelpers.cpp index 00b05565d68..f670c5a323a 100644 --- a/toolkit/components/places/src/AsyncFaviconHelpers.cpp +++ b/toolkit/components/places/src/AsyncFaviconHelpers.cpp @@ -49,6 +49,7 @@ #include "nsNavHistory.h" #include "nsNavBookmarks.h" #include "nsFaviconService.h" +#include "nsIAsyncVerifyRedirectCallback.h" #include "nsCycleCollectionParticipant.h" @@ -758,11 +759,13 @@ FetchNetworkIconStep::GetInterface(const nsIID& uuid, 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; } diff --git a/toolkit/components/places/tests/mochitest/bug_411966/redirect.js b/toolkit/components/places/tests/mochitest/bug_411966/redirect.js index e7b6c1a481a..f141db08f3f 100644 --- a/toolkit/components/places/tests/mochitest/bug_411966/redirect.js +++ b/toolkit/components/places/tests/mochitest/bug_411966/redirect.js @@ -111,11 +111,12 @@ StreamListener.prototype = { }, // 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 diff --git a/toolkit/components/places/tests/network/test_history_redirects.js b/toolkit/components/places/tests/network/test_history_redirects.js index 23f9b0fc0c8..016b4023dd6 100644 --- a/toolkit/components/places/tests/network/test_history_redirects.js +++ b/toolkit/components/places/tests/network/test_history_redirects.js @@ -219,9 +219,10 @@ ChannelListener.prototype = { }, // 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); }, }; diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index 6355f7b9e00..060a3ea39e6 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -375,9 +375,10 @@ loadListener.prototype = { }, // 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 diff --git a/toolkit/mozapps/shared/CertUtils.jsm b/toolkit/mozapps/shared/CertUtils.jsm index 6afc532a21f..a02f16f7c91 100644 --- a/toolkit/mozapps/shared/CertUtils.jsm +++ b/toolkit/mozapps/shared/CertUtils.jsm @@ -143,7 +143,7 @@ function BadCertHandler(aAllowNonBuiltInCerts) { BadCertHandler.prototype = { // nsIChannelEventSink - onChannelRedirect: function(oldChannel, newChannel, flags) { + asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) { if (this.allowNonBuiltInCerts) return; @@ -152,7 +152,9 @@ BadCertHandler.prototype = { // 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) { diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp index 117252893ea..78e4d9b1c76 100644 --- a/uriloader/base/nsDocLoader.cpp +++ b/uriloader/base/nsDocLoader.cpp @@ -64,6 +64,7 @@ #include "nsIDOMDocument.h" #include "nsIDocument.h" #include "nsPresContext.h" +#include "nsIAsyncVerifyRedirectCallback.h" static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID); @@ -1584,9 +1585,10 @@ PRInt64 nsDocLoader::CalculateMaxProgress() 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) { @@ -1611,6 +1613,7 @@ NS_IMETHODIMP nsDocLoader::OnChannelRedirect(nsIChannel *aOldChannel, FireOnStateChange(this, aOldChannel, stateFlags, NS_OK); } + cb->OnRedirectVerifyCallback(NS_OK); return NS_OK; } diff --git a/uriloader/prefetch/nsOfflineCacheUpdate.cpp b/uriloader/prefetch/nsOfflineCacheUpdate.cpp index 15158ed083f..d411f837726 100644 --- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp +++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp @@ -70,6 +70,7 @@ #include "nsThreadUtils.h" #include "nsProxyRelease.h" #include "prlog.h" +#include "nsIAsyncVerifyRedirectCallback.h" static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull; @@ -283,13 +284,16 @@ nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult) //----------------------------------------------------------------------------- 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; } @@ -491,9 +495,10 @@ nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult) //----------------------------------------------------------------------------- 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 @@ -539,6 +544,7 @@ nsOfflineCacheUpdateItem::OnChannelRedirect(nsIChannel *aOldChannel, mChannel = aNewChannel; + cb->OnRedirectVerifyCallback(NS_OK); return NS_OK; } diff --git a/uriloader/prefetch/nsPrefetchService.cpp b/uriloader/prefetch/nsPrefetchService.cpp index af9db0d9cf9..9da5e59444c 100644 --- a/uriloader/prefetch/nsPrefetchService.cpp +++ b/uriloader/prefetch/nsPrefetchService.cpp @@ -60,6 +60,7 @@ #include "prtime.h" #include "prlog.h" #include "plstr.h" +#include "nsIAsyncVerifyRedirectCallback.h" #if defined(PR_LOGGING) // @@ -363,9 +364,10 @@ nsPrefetchNode::GetInterface(const nsIID &aIID, void **aResult) //----------------------------------------------------------------------------- NS_IMETHODIMP -nsPrefetchNode::OnChannelRedirect(nsIChannel *aOldChannel, - nsIChannel *aNewChannel, - PRUint32 aFlags) +nsPrefetchNode::AsyncOnChannelRedirect(nsIChannel *aOldChannel, + nsIChannel *aNewChannel, + PRUint32 aFlags, + nsIAsyncVerifyRedirectCallback *callback) { nsCOMPtr newURI; nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI)); @@ -395,6 +397,7 @@ nsPrefetchNode::OnChannelRedirect(nsIChannel *aOldChannel, mChannel = aNewChannel; + callback->OnRedirectVerifyCallback(NS_OK); return NS_OK; } diff --git a/xpinstall/src/nsXPInstallManager.cpp b/xpinstall/src/nsXPInstallManager.cpp index 98869298091..c9c1f5fbf62 100644 --- a/xpinstall/src/nsXPInstallManager.cpp +++ b/xpinstall/src/nsXPInstallManager.cpp @@ -91,6 +91,7 @@ #include "CertReader.h" #include "nsEmbedCID.h" +#include "nsIAsyncVerifyRedirectCallback.h" #define PREF_XPINSTALL_ENABLED "xpinstall.enabled" #define PREF_XPINSTALL_CONFIRM_DLG "xpinstall.dialog.confirm" @@ -1323,11 +1324,19 @@ nsXPInstallManager::GetInterface(const nsIID & 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; }