/* -*- 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 "ParentChannelListener.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/ServiceWorkerInterceptController.h" #include "mozilla/dom/ServiceWorkerUtils.h" #include "mozilla/net/HttpChannelParent.h" #include "mozilla/net/RedirectChannelRegistrar.h" #include "mozilla/SchedulerGroup.h" #include "nsIHttpHeaderVisitor.h" #include "nsIPrompt.h" #include "nsISecureBrowserUI.h" #include "nsIWindowWatcher.h" #include "nsQueryObject.h" #include "nsIAuthPrompt.h" #include "nsIAuthPrompt2.h" #include "nsIPromptFactory.h" #include "Element.h" #include "nsILoginManagerAuthPrompter.h" #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/LoadURIOptionsBinding.h" #include "nsIWebNavigation.h" using mozilla::dom::ServiceWorkerInterceptController; namespace mozilla { namespace net { ParentChannelListener::ParentChannelListener( nsIStreamListener* aListener, dom::CanonicalBrowsingContext* aBrowsingContext) : mNextListener(aListener), mBrowsingContext(aBrowsingContext) { LOG(("ParentChannelListener::ParentChannelListener [this=%p, next=%p]", this, aListener)); mInterceptController = new ServiceWorkerInterceptController(); } ParentChannelListener::~ParentChannelListener() { LOG(("ParentChannelListener::~ParentChannelListener %p", this)); } //----------------------------------------------------------------------------- // ParentChannelListener::nsISupports //----------------------------------------------------------------------------- NS_IMPL_ADDREF(ParentChannelListener) NS_IMPL_RELEASE(ParentChannelListener) NS_INTERFACE_MAP_BEGIN(ParentChannelListener) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIStreamListener) NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener) NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController) NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAuthPromptProvider, mBrowsingContext) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY_CONCRETE(ParentChannelListener) NS_INTERFACE_MAP_END //----------------------------------------------------------------------------- // ParentChannelListener::nsIRequestObserver //----------------------------------------------------------------------------- NS_IMETHODIMP ParentChannelListener::OnStartRequest(nsIRequest* aRequest) { if (!mNextListener) return NS_ERROR_UNEXPECTED; // If we're not a multi-part channel, then we can drop mListener and break the // reference cycle. If we are, then this might be called again, so wait for // OnAfterLastPart instead. nsCOMPtr multiPartChannel = do_QueryInterface(aRequest); if (multiPartChannel) { mIsMultiPart = true; } LOG(("ParentChannelListener::OnStartRequest [this=%p]\n", this)); return mNextListener->OnStartRequest(aRequest); } NS_IMETHODIMP ParentChannelListener::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { if (!mNextListener) return NS_ERROR_UNEXPECTED; LOG(("ParentChannelListener::OnStopRequest: [this=%p status=%" PRIu32 "]\n", this, static_cast(aStatusCode))); nsresult rv = mNextListener->OnStopRequest(aRequest, aStatusCode); if (!mIsMultiPart) { mNextListener = nullptr; } return rv; } //----------------------------------------------------------------------------- // ParentChannelListener::nsIStreamListener //----------------------------------------------------------------------------- NS_IMETHODIMP ParentChannelListener::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, uint64_t aOffset, uint32_t aCount) { if (!mNextListener) return NS_ERROR_UNEXPECTED; LOG(("ParentChannelListener::OnDataAvailable [this=%p]\n", this)); return mNextListener->OnDataAvailable(aRequest, aInputStream, aOffset, aCount); } //----------------------------------------------------------------------------- // ParentChannelListener::nsIMultiPartChannelListener //----------------------------------------------------------------------------- NS_IMETHODIMP ParentChannelListener::OnAfterLastPart(nsresult aStatus) { nsCOMPtr multiListener = do_QueryInterface(mNextListener); if (multiListener) { multiListener->OnAfterLastPart(aStatus); } mNextListener = nullptr; return NS_OK; } //----------------------------------------------------------------------------- // ParentChannelListener::nsIInterfaceRequestor //----------------------------------------------------------------------------- NS_IMETHODIMP ParentChannelListener::GetInterface(const nsIID& aIID, void** result) { if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController))) { return QueryInterface(aIID, result); } if (mBrowsingContext && aIID.Equals(NS_GET_IID(nsIPrompt))) { nsCOMPtr frameElement = mBrowsingContext->Top()->GetEmbedderElement(); if (frameElement) { nsCOMPtr win = frameElement->OwnerDoc()->GetWindow(); NS_ENSURE_TRUE(win, NS_ERROR_NO_INTERFACE); nsresult rv; nsCOMPtr wwatch = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); if (NS_WARN_IF(!NS_SUCCEEDED(rv))) { return NS_ERROR_NO_INTERFACE; } nsCOMPtr prompt; rv = wwatch->GetNewPrompter(win, getter_AddRefs(prompt)); if (NS_WARN_IF(!NS_SUCCEEDED(rv))) { return NS_ERROR_NO_INTERFACE; } prompt.forget(result); return NS_OK; } } if (mBrowsingContext && (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || aIID.Equals(NS_GET_IID(nsIAuthPrompt2)))) { nsresult rv = GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL, aIID, result); if (NS_FAILED(rv)) { return NS_ERROR_NO_INTERFACE; } return NS_OK; } nsCOMPtr ir; if (mNextListener && NS_SUCCEEDED(CallQueryInterface(mNextListener.get(), getter_AddRefs(ir)))) { return ir->GetInterface(aIID, result); } return NS_NOINTERFACE; } void ParentChannelListener::SetListenerAfterRedirect( nsIStreamListener* aListener) { mNextListener = aListener; } //----------------------------------------------------------------------------- // ParentChannelListener::nsINetworkInterceptController //----------------------------------------------------------------------------- NS_IMETHODIMP ParentChannelListener::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel, bool* aShouldIntercept) { // If parent-side interception is enabled just forward to the real // network controler. if (mInterceptController) { return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel, aShouldIntercept); } *aShouldIntercept = false; return NS_OK; } NS_IMETHODIMP ParentChannelListener::ChannelIntercepted(nsIInterceptedChannel* aChannel) { // If parent-side interception is enabled just forward to the real // network controler. if (mInterceptController) { return mInterceptController->ChannelIntercepted(aChannel); } return NS_OK; } //----------------------------------------------------------------------------- // ParentChannelListener::nsIAuthPromptProvider // NS_IMETHODIMP ParentChannelListener::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid, void** aResult) { if (!mBrowsingContext) { return NS_ERROR_NOT_AVAILABLE; } // we're either allowing auth, or it's a proxy request nsresult rv; nsCOMPtr wwatch = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr window; RefPtr frame = mBrowsingContext->Top()->GetEmbedderElement(); if (frame) window = frame->OwnerDoc()->GetWindow(); // Get an auth prompter for our window so that the parenting // of the dialogs works as it should when using tabs. nsCOMPtr prompt; rv = wwatch->GetPrompt(window, iid, getter_AddRefs(prompt)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr prompter = do_QueryInterface(prompt); if (prompter) { prompter->SetBrowser(frame); } *aResult = prompt.forget().take(); return NS_OK; } //----------------------------------------------------------------------------- // ParentChannelListener::nsIThreadRetargetableStreamListener // NS_IMETHODIMP ParentChannelListener::CheckListenerChain() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr listener = do_QueryInterface(mNextListener); if (!listener) { return NS_ERROR_NO_INTERFACE; } return listener->CheckListenerChain(); } } // namespace net } // namespace mozilla