Bug 1574372 - Add support to nsDocumentOpenInfo for overriding some functionality that we want to modify in the parent. r=bzbarsky

Differential Revision: https://phabricator.services.mozilla.com/D56136

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Matt Woodrow 2019-12-16 21:39:55 +00:00
parent d1851fa263
commit 3f6ef264a3
2 changed files with 130 additions and 29 deletions

View File

@ -24,6 +24,7 @@
#include "nsIDocShell.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsIChildChannel.h"
#include "nsExternalHelperAppService.h"
#include "nsString.h"
#include "nsThreadUtils.h"
@ -77,6 +78,14 @@ nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
mURILoader(aURILoader),
mDataConversionDepthLimit(sConvertDataLimit) {}
nsDocumentOpenInfo::nsDocumentOpenInfo(uint32_t aFlags,
bool aAllowListenerConversions)
: m_originalContext(nullptr),
mFlags(aFlags),
mURILoader(nullptr),
mDataConversionDepthLimit(sConvertDataLimit),
mAllowListenerConversions(aAllowListenerConversions) {}
nsDocumentOpenInfo::~nsDocumentOpenInfo() {}
nsresult nsDocumentOpenInfo::Prepare() {
@ -169,8 +178,8 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest* request) {
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(status)) {
LOG_ERROR(
(" Request failed, status: 0x%08" PRIX32, static_cast<uint32_t>(rv)));
LOG_ERROR((" Request failed, status: 0x%08" PRIX32,
static_cast<uint32_t>(status)));
//
// The transaction has already reported an error - so it will be torn
@ -242,6 +251,7 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request,
mContentType.Truncate();
listener->OnStopRequest(request, aStatus);
}
mUsedContentHandler = false;
// Remember...
// In the case of multiplexed streams (such as multipart/x-mixed-replace)
@ -300,7 +310,7 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
// First step: See whether m_contentListener wants to handle this
// content type.
//
if (m_contentListener && TryContentListener(m_contentListener, aChannel)) {
if (TryDefaultContentListener(aChannel)) {
LOG((" Success! Our default listener likes this type"));
// All done here
return NS_OK;
@ -313,7 +323,7 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
// Second step: See whether some other registered listener wants
// to handle this content type.
//
int32_t count = mURILoader->m_listeners.Count();
int32_t count = mURILoader ? mURILoader->m_listeners.Count() : 0;
nsCOMPtr<nsIURIContentListener> listener;
for (int32_t i = 0; i < count; i++) {
listener = do_QueryReferent(mURILoader->m_listeners[i]);
@ -364,6 +374,10 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
do_CreateInstance(handlerContractID.get());
if (contentHandler) {
LOG((" Content handler found"));
// Note that m_originalContext can be nullptr when running this in
// the parent process on behalf on a docshell in the content process,
// and in that case we only support content handlers that don't need
// the context.
rv = contentHandler->HandleContent(mContentType.get(),
m_originalContext, request);
// XXXbz returning an error code to represent handling the
@ -376,6 +390,7 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
request->Cancel(rv);
} else {
LOG((" Content handler taking over load"));
mUsedContentHandler = true;
}
return rv;
@ -397,13 +412,7 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
// XXXbz have to be careful here; may end up in some sort of bizarre
// infinite decoding loop.
if (mContentType != anyType) {
rv = ConvertData(request, m_contentListener, mContentType, anyType);
if (NS_FAILED(rv)) {
m_targetStreamListener = nullptr;
} else if (m_targetStreamListener) {
// We found a converter for this MIME type. We'll just pump data into
// it and let the downstream nsDocumentOpenInfo handle things.
LOG((" Converter taking over now"));
if (TryStreamConversion(aChannel)) {
return NS_OK;
}
}
@ -455,10 +464,7 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
}
rv = helperAppService->DoContent(mContentType, request, m_originalContext,
false, nullptr,
getter_AddRefs(m_targetStreamListener));
if (NS_FAILED(rv)) {
if (!TryExternalHelperApp(helperAppService, aChannel)) {
request->SetLoadFlags(loadFlags);
m_targetStreamListener = nullptr;
}
@ -470,6 +476,14 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
return rv;
}
bool nsDocumentOpenInfo::TryExternalHelperApp(
nsIExternalHelperAppService* aHelperAppService, nsIChannel* aChannel) {
nsresult rv = aHelperAppService->DoContent(
mContentType, aChannel, m_originalContext, false, nullptr,
getter_AddRefs(m_targetStreamListener));
return NS_SUCCEEDED(rv);
}
nsresult nsDocumentOpenInfo::ConvertData(nsIRequest* request,
nsIURIContentListener* aListener,
const nsACString& aSrcContentType,
@ -479,9 +493,10 @@ nsresult nsDocumentOpenInfo::ConvertData(nsIRequest* request,
PromiseFlatCString(aOutContentType).get()));
if (mDataConversionDepthLimit == 0) {
LOG((
"[0x%p] nsDocumentOpenInfo::ConvertData - reached the recursion limit!",
this));
LOG(
("[0x%p] nsDocumentOpenInfo::ConvertData - reached the recursion "
"limit!",
this));
// This will fall back to external helper app handling.
return NS_ERROR_ABORT;
}
@ -505,16 +520,15 @@ nsresult nsDocumentOpenInfo::ConvertData(nsIRequest* request,
// stream is split up into multiple destination streams. This
// intermediate instance is used to target these "decoded" streams...
//
RefPtr<nsDocumentOpenInfo> nextLink =
new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
RefPtr<nsDocumentOpenInfo> nextLink = Clone();
LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
// Decrease the conversion recursion limit by one to prevent infinite loops.
nextLink->mDataConversionDepthLimit = mDataConversionDepthLimit - 1;
// Make sure nextLink starts with the contentListener that said it wanted the
// results of this decode.
// Make sure nextLink starts with the contentListener that said it wanted
// the results of this decode.
nextLink->m_contentListener = aListener;
// Also make sure it has to look for a stream listener to pump data into.
nextLink->m_targetStreamListener = nullptr;
@ -536,6 +550,20 @@ nsresult nsDocumentOpenInfo::ConvertData(nsIRequest* request,
getter_AddRefs(m_targetStreamListener));
}
bool nsDocumentOpenInfo::TryStreamConversion(nsIChannel* aChannel) {
NS_NAMED_LITERAL_CSTRING(anyType, "*/*");
nsresult rv = ConvertData(aChannel, m_contentListener, mContentType, anyType);
if (NS_FAILED(rv)) {
m_targetStreamListener = nullptr;
} else if (m_targetStreamListener) {
// We found a converter for this MIME type. We'll just pump data into
// it and let the downstream nsDocumentOpenInfo handle things.
LOG((" Converter taking over now"));
return true;
}
return false;
}
bool nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
nsIChannel* aChannel) {
LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x", this,
@ -563,7 +591,10 @@ bool nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
// Need to do a conversion here.
nsresult rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
nsresult rv = NS_ERROR_NOT_AVAILABLE;
if (mAllowListenerConversions) {
rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
}
if (NS_FAILED(rv)) {
// No conversion path -- we don't want this listener, if we got one
@ -625,6 +656,13 @@ bool nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
return true;
}
bool nsDocumentOpenInfo::TryDefaultContentListener(nsIChannel* aChannel) {
if (m_contentListener) {
return TryContentListener(m_contentListener, aChannel);
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Implementation of nsURILoader
///////////////////////////////////////////////////////////////////////////////////////////////
@ -680,8 +718,9 @@ NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel* channel, uint32_t aFlags,
if (NS_SUCCEEDED(rv)) {
if (aFlags & nsIURILoader::REDIRECTED_CHANNEL) {
// Our channel was redirected from another process, so doesn't need to be
// opened again. However, it does need its listener hooked up correctly.
// Our channel was redirected from another process, so doesn't need to
// be opened again. However, it does need its listener hooked up
// correctly.
nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(channel);
MOZ_ASSERT(childChannel);
if (!childChannel) {
@ -726,7 +765,7 @@ nsresult nsURILoader::OpenChannel(nsIChannel* channel, uint32_t aFlags,
LOG(("nsURILoader::OpenChannel for %s", spec.get()));
}
// Let the window context's uriListener know that the open is starting. This
// Let the window context's uriListener know that the open is starting. This
// gives that window a chance to abort the load process.
nsCOMPtr<nsIURIContentListener> winContextListener(
do_GetInterface(aWindowContext));

View File

@ -17,6 +17,7 @@
#include "mozilla/Attributes.h"
#include "nsIStreamListener.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsIExternalHelperAppService.h"
#include "mozilla/Logging.h"
@ -62,13 +63,14 @@ class nsURILoader final : public nsIURILoader {
* Each instance remains alive until its target URL has been loaded
* (or aborted).
*/
class nsDocumentOpenInfo final : public nsIStreamListener,
public nsIThreadRetargetableStreamListener {
class nsDocumentOpenInfo : public nsIStreamListener,
public nsIThreadRetargetableStreamListener {
public:
// Real constructor
// aFlags is a combination of the flags on nsIURILoader
nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext, uint32_t aFlags,
nsURILoader* aURILoader);
nsDocumentOpenInfo(uint32_t aFlags, bool aAllowListenerConversions);
NS_DECL_THREADSAFE_ISUPPORTS
@ -99,6 +101,44 @@ class nsDocumentOpenInfo final : public nsIStreamListener,
bool TryContentListener(nsIURIContentListener* aListener,
nsIChannel* aChannel);
/**
* Virtual helper functions for content that we expect to be
* overriden when running in the parent process on behalf of
* a content process docshell.
* We also expect nsIStreamListener functions to be overriden
* to add functionality.
*/
/**
* Attempt to create a steam converter converting from the
* current mContentType into something else.
* Sets m_targetStreamListener if it succeeds.
*/
virtual bool TryStreamConversion(nsIChannel* aChannel);
/**
* Attempt to use the default content listener as our stream
* listener.
* Sets m_targetStreamListener if it succeeds.
*/
virtual bool TryDefaultContentListener(nsIChannel* aChannel);
/**
* Attempt to pass aChannel onto the external helper app service.
* Sets m_targetStreamListener if it succeeds.
*/
virtual bool TryExternalHelperApp(
nsIExternalHelperAppService* aHelperAppService, nsIChannel* aChannel);
/**
* Create another nsDocumentOpenInfo like this one, so that we can chain
* them together when we use a stream converter and don't know what the
* converted content type is until the converter outputs OnStartRequest.
*/
virtual nsDocumentOpenInfo* Clone() {
return new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
}
// nsIRequestObserver methods:
NS_DECL_NSIREQUESTOBSERVER
@ -107,13 +147,16 @@ class nsDocumentOpenInfo final : public nsIStreamListener,
// nsIThreadRetargetableStreamListener
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
protected:
~nsDocumentOpenInfo();
virtual ~nsDocumentOpenInfo();
protected:
/**
* The first content listener to try dispatching data to. Typically
* the listener associated with the entity that originated the load.
* This can be nullptr when running in the parent process for a content
* process docshell.
*/
nsCOMPtr<nsIURIContentListener> m_contentListener;
@ -126,6 +169,8 @@ class nsDocumentOpenInfo final : public nsIStreamListener,
/**
* A pointer to the entity that originated the load. We depend on getting
* things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
* This can be nullptr when running in the parent process for a content
* process docshell.
*/
nsCOMPtr<nsIInterfaceRequestor> m_originalContext;
@ -145,6 +190,8 @@ class nsDocumentOpenInfo final : public nsIStreamListener,
/**
* Reference to the URILoader service so we can access its list of
* nsIURIContentListeners.
* This can be nullptr when running in the parent process for a content
* process docshell.
*/
RefPtr<nsURILoader> mURILoader;
@ -152,6 +199,21 @@ class nsDocumentOpenInfo final : public nsIStreamListener,
* Limit of data conversion depth to prevent infinite conversion loops
*/
uint32_t mDataConversionDepthLimit;
/**
* Set to true if OnStartRequest handles the content using an
* nsIContentHandler, and the content is consumed despite
* m_targetStreamListener being nullptr.
*/
bool mUsedContentHandler = false;
/**
* True if we allow nsIURIContentListeners to return a requested
* input typeToUse, and attempt to create a matching stream converter.
* This is false when running in the parent process for a content process
* docshell
*/
bool mAllowListenerConversions = true;
};
#endif /* nsURILoader_h__ */