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