michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set sw=2 ts=8 et tw=80 : */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: michael@0: #include "HttpChannelParentListener.h" michael@0: #include "mozilla/net/HttpChannelParent.h" michael@0: #include "mozilla/unused.h" michael@0: #include "nsIRedirectChannelRegistrar.h" michael@0: #include "nsIHttpEventSink.h" michael@0: michael@0: using mozilla::unused; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel) michael@0: : mNextListener(aInitialChannel) michael@0: , mRedirectChannelId(0) michael@0: , mSuspendedForDiversion(false) michael@0: { michael@0: } michael@0: michael@0: HttpChannelParentListener::~HttpChannelParentListener() michael@0: { michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParentListener::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(HttpChannelParentListener, michael@0: nsIInterfaceRequestor, michael@0: nsIStreamListener, michael@0: nsIRequestObserver, michael@0: nsIChannelEventSink, michael@0: nsIRedirectResultListener) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParentListener::nsIRequestObserver michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParentListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) michael@0: { michael@0: MOZ_RELEASE_ASSERT(!mSuspendedForDiversion, michael@0: "Cannot call OnStartRequest if suspended for diversion!"); michael@0: michael@0: if (!mNextListener) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: LOG(("HttpChannelParentListener::OnStartRequest [this=%p]\n", this)); michael@0: return mNextListener->OnStartRequest(aRequest, aContext); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsresult aStatusCode) michael@0: { michael@0: MOZ_RELEASE_ASSERT(!mSuspendedForDiversion, michael@0: "Cannot call OnStopRequest if suspended for diversion!"); michael@0: michael@0: if (!mNextListener) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: LOG(("HttpChannelParentListener::OnStopRequest: [this=%p status=%ul]\n", michael@0: this, aStatusCode)); michael@0: nsresult rv = mNextListener->OnStopRequest(aRequest, aContext, aStatusCode); michael@0: michael@0: mNextListener = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParentListener::nsIStreamListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsIInputStream *aInputStream, michael@0: uint64_t aOffset, michael@0: uint32_t aCount) michael@0: { michael@0: MOZ_RELEASE_ASSERT(!mSuspendedForDiversion, michael@0: "Cannot call OnDataAvailable if suspended for diversion!"); michael@0: michael@0: if (!mNextListener) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: LOG(("HttpChannelParentListener::OnDataAvailable [this=%p]\n", this)); michael@0: return mNextListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParentListener::nsIInterfaceRequestor michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result) michael@0: { michael@0: if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)) || michael@0: aIID.Equals(NS_GET_IID(nsIHttpEventSink)) || michael@0: aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) michael@0: { michael@0: return QueryInterface(aIID, result); michael@0: } michael@0: michael@0: nsCOMPtr ir; michael@0: if (mNextListener && michael@0: NS_SUCCEEDED(CallQueryInterface(mNextListener.get(), michael@0: getter_AddRefs(ir)))) michael@0: { michael@0: return ir->GetInterface(aIID, result); michael@0: } michael@0: michael@0: return NS_NOINTERFACE; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParentListener::nsIChannelEventSink michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParentListener::AsyncOnChannelRedirect( michael@0: nsIChannel *oldChannel, michael@0: nsIChannel *newChannel, michael@0: uint32_t redirectFlags, michael@0: nsIAsyncVerifyRedirectCallback* callback) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Register the new channel and obtain id for it michael@0: nsCOMPtr registrar = michael@0: do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = registrar->RegisterChannel(newChannel, &mRedirectChannelId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: LOG(("Registered %p channel under id=%d", newChannel, mRedirectChannelId)); michael@0: michael@0: nsCOMPtr activeRedirectingChannel = michael@0: do_QueryInterface(mNextListener); michael@0: if (!activeRedirectingChannel) { michael@0: NS_RUNTIMEABORT("Channel got a redirect response, but doesn't implement " michael@0: "nsIParentRedirectingChannel to handle it."); michael@0: } michael@0: michael@0: return activeRedirectingChannel->StartRedirect(mRedirectChannelId, michael@0: newChannel, michael@0: redirectFlags, michael@0: callback); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // HttpChannelParentListener::nsIRedirectResultListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: HttpChannelParentListener::OnRedirectResult(bool succeeded) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr redirectChannel; michael@0: if (mRedirectChannelId) { michael@0: nsCOMPtr registrar = michael@0: do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = registrar->GetParentChannel(mRedirectChannelId, michael@0: getter_AddRefs(redirectChannel)); michael@0: if (NS_FAILED(rv) || !redirectChannel) { michael@0: // Redirect might get canceled before we got AsyncOnChannelRedirect michael@0: LOG(("Registered parent channel not found under id=%d", mRedirectChannelId)); michael@0: michael@0: nsCOMPtr newChannel; michael@0: rv = registrar->GetRegisteredChannel(mRedirectChannelId, michael@0: getter_AddRefs(newChannel)); michael@0: MOZ_ASSERT(newChannel, "Already registered channel not found"); michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: newChannel->Cancel(NS_BINDING_ABORTED); michael@0: } michael@0: michael@0: // Release all previously registered channels, they are no longer need to be michael@0: // kept in the registrar from this moment. michael@0: registrar->DeregisterChannels(mRedirectChannelId); michael@0: michael@0: mRedirectChannelId = 0; michael@0: } michael@0: michael@0: if (!redirectChannel) { michael@0: succeeded = false; michael@0: } michael@0: michael@0: nsCOMPtr activeRedirectingChannel = michael@0: do_QueryInterface(mNextListener); michael@0: MOZ_ASSERT(activeRedirectingChannel, michael@0: "Channel finished a redirect response, but doesn't implement " michael@0: "nsIParentRedirectingChannel to complete it."); michael@0: michael@0: if (activeRedirectingChannel) { michael@0: activeRedirectingChannel->CompleteRedirect(succeeded); michael@0: } else { michael@0: succeeded = false; michael@0: } michael@0: michael@0: if (succeeded) { michael@0: // Switch to redirect channel and delete the old one. michael@0: nsCOMPtr parent; michael@0: parent = do_QueryInterface(mNextListener); michael@0: MOZ_ASSERT(parent); michael@0: parent->Delete(); michael@0: mNextListener = do_QueryInterface(redirectChannel); michael@0: MOZ_ASSERT(mNextListener); michael@0: redirectChannel->SetParentListener(this); michael@0: } else if (redirectChannel) { michael@0: // Delete the redirect target channel: continue using old channel michael@0: redirectChannel->Delete(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: HttpChannelParentListener::SuspendForDiversion() michael@0: { michael@0: if (NS_WARN_IF(mSuspendedForDiversion)) { michael@0: MOZ_ASSERT(!mSuspendedForDiversion, "Cannot SuspendForDiversion twice!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // While this is set, no OnStart/OnData/OnStop callbacks should be forwarded michael@0: // to mNextListener. michael@0: mSuspendedForDiversion = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: HttpChannelParentListener::ResumeForDiversion() michael@0: { michael@0: MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!"); michael@0: michael@0: // Allow OnStart/OnData/OnStop callbacks to be forwarded to mNextListener. michael@0: mSuspendedForDiversion = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: HttpChannelParentListener::DivertTo(nsIStreamListener* aListener) michael@0: { michael@0: MOZ_ASSERT(aListener); michael@0: MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!"); michael@0: michael@0: mNextListener = aListener; michael@0: michael@0: return ResumeForDiversion(); michael@0: } michael@0: michael@0: }} // mozilla::net