gecko-dev/uriloader/base/nsURILoader.cpp

785 lines
28 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsURILoader.h"
#include "nsIURIContentListener.h"
#include "nsIContentHandler.h"
#include "nsILoadGroup.h"
#include "nsIDocumentLoader.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIStreamListener.h"
#include "nsIURI.h"
#include "nsIChannel.h"
#include "nsIInterfaceRequestor.h"
#include "nsIProgressEventSink.h"
#include "nsIInputStream.h"
#include "nsIStreamConverterService.h"
#include "nsWeakReference.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "nsVoidArray.h"
#include "nsXPIDLString.h"
#include "nsString.h"
#include "nsIDOMWindow.h"
#include "nsIUnkContentTypeHandler.h"
static NS_DEFINE_CID(kURILoaderCID, NS_URI_LOADER_CID);
static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
///////////////////////////////////////////////////////////////////
// The nsLoadCookie is an nsISupports class that is opaque to users
// of the URI Loader service. The intent is to use the load cookie
// as a "load context" so we can keep a load group and a doc loader
// associated with multiple openURI requests on the uri loader from
// the same caller. i.e. if the caller has a load cookie, they pass
// it into the uri loader so we can group this request with other
// requests from the same caller. If you don't have a load cookie yet,
// we will provide one for you.
///////////////////////////////////////////////////////////////////
class nsLoadCookie : public nsIInterfaceRequestor,
public nsSupportsWeakReference
{
NS_DECL_ISUPPORTS
NS_DECL_NSIINTERFACEREQUESTOR
nsLoadCookie();
nsresult Init(nsISupports * aParentCookie);
protected:
virtual ~nsLoadCookie();
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIDocumentLoader> mDocLoader;
};
NS_IMPL_ADDREF(nsLoadCookie);
NS_IMPL_RELEASE(nsLoadCookie);
NS_INTERFACE_MAP_BEGIN(nsLoadCookie)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END
nsLoadCookie::nsLoadCookie()
{
NS_INIT_ISUPPORTS();
}
nsLoadCookie::~nsLoadCookie()
{
// if we are going away, then the doc loader we're associated with needs to be
// destroyed....
if (mDocLoader)
mDocLoader->Destroy();
}
nsresult
nsLoadCookie::Init(nsISupports * aParentCookie)
{
nsCOMPtr<nsIDocumentLoader> parentDocLoader;
nsresult rv = NS_OK;
if (aParentCookie) // if we had a parent cookie use it to help with the creation process
parentDocLoader = do_GetInterface(aParentCookie);
if (!parentDocLoader)
parentDocLoader = do_GetService(NS_DOCUMENTLOADER_SERVICE_PROGID, &rv);
NS_ENSURE_TRUE(parentDocLoader, NS_ERROR_FAILURE);
parentDocLoader->CreateDocumentLoader(getter_AddRefs(mDocLoader));
// now turn around and get the load group that the doc loader created...
NS_ENSURE_SUCCESS(mDocLoader->GetLoadGroup(getter_AddRefs(mLoadGroup)), NS_ERROR_FAILURE);
return rv;
}
NS_IMETHODIMP
nsLoadCookie::GetInterface(const nsIID &aIID, void** aInstancePtr)
{
NS_ENSURE_ARG_POINTER(aInstancePtr);
if(aIID.Equals(NS_GET_IID(nsILoadGroup)))
{
*aInstancePtr = mLoadGroup;
NS_ADDREF((nsISupports*)*aInstancePtr);
return NS_OK;
}
else if(aIID.Equals(NS_GET_IID(nsIDocumentLoader)))
{
*aInstancePtr = mDocLoader;
NS_ADDREF((nsISupports*)*aInstancePtr);
return NS_OK;
}
else
return NS_ERROR_NO_INTERFACE;
}
/*
* The nsDocumentOpenInfo contains the state required when a single document
* is being opened in order to discover the content type... Each instance remains alive until its target URL has
* been loaded (or aborted).
*
*/
class nsDocumentOpenInfo : public nsIStreamListener
{
public:
nsDocumentOpenInfo();
nsresult Init(nsISupports * aWindowContext);
NS_DECL_ISUPPORTS
nsresult Open(nsIChannel * aChannel,
nsURILoadCommand aCommand,
const char * aWindowTarget,
nsISupports * aWindowContext);
nsresult DispatchContent(nsIChannel * aChannel, nsISupports * aCtxt);
nsresult RetargetOutput(nsIChannel * aChannel, const char * aSrcContentType,
const char * aOutContentType, nsIStreamListener * aStreamListener);
// nsIStreamObserver methods:
NS_DECL_NSISTREAMOBSERVER
// nsIStreamListener methods:
NS_DECL_NSISTREAMLISTENER
protected:
virtual ~nsDocumentOpenInfo();
nsDocumentOpenInfo* Clone();
nsresult InvokeUnknownContentHandler(nsIChannel * aChannel, const char * aContentType, nsIDOMWindow * aDomWindow);
protected:
nsCOMPtr<nsIURIContentListener> m_contentListener;
nsCOMPtr<nsIStreamListener> m_targetStreamListener;
nsURILoadCommand mCommand;
nsCString m_windowTarget;
};
NS_IMPL_THREADSAFE_ADDREF(nsDocumentOpenInfo);
NS_IMPL_THREADSAFE_RELEASE(nsDocumentOpenInfo);
NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamObserver)
NS_INTERFACE_MAP_ENTRY(nsIStreamObserver)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_END_THREADSAFE
nsDocumentOpenInfo::nsDocumentOpenInfo()
{
NS_INIT_ISUPPORTS();
}
nsDocumentOpenInfo::~nsDocumentOpenInfo()
{
}
nsresult nsDocumentOpenInfo::Init(nsISupports * aWindowContext)
{
// ask the window context if it has a uri content listener...
nsresult rv = NS_OK;
m_contentListener = do_GetInterface(aWindowContext, &rv);
// now determine if the
return rv;
}
nsDocumentOpenInfo* nsDocumentOpenInfo::Clone()
{
nsDocumentOpenInfo* newObject;
newObject = new nsDocumentOpenInfo();
if (newObject) {
newObject->m_contentListener = m_contentListener;
newObject->mCommand = mCommand;
newObject->m_windowTarget = m_windowTarget;
}
return newObject;
}
nsresult nsDocumentOpenInfo::Open(nsIChannel * aChannel,
nsURILoadCommand aCommand,
const char * aWindowTarget,
nsISupports * aWindowContext)
{
// this method is not complete!!! Eventually, we should first go
// to the content listener and ask them for a protocol handler...
// if they don't give us one, we need to go to the registry and get
// the preferred protocol handler.
// But for now, I'm going to let necko do the work for us....
nsresult rv = NS_OK;
// store any local state
m_windowTarget = aWindowTarget;
mCommand = aCommand;
// now jut open the channel!
if (aChannel)
rv = aChannel->AsyncRead(0, -1, nsnull, this);
return rv;
}
NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIChannel * aChannel, nsISupports * aCtxt)
{
nsresult rv = NS_OK;
rv = DispatchContent(aChannel, aCtxt);
if (m_targetStreamListener)
rv = m_targetStreamListener->OnStartRequest(aChannel, aCtxt);
return rv;
}
NS_IMETHODIMP nsDocumentOpenInfo::OnDataAvailable(nsIChannel * aChannel, nsISupports * aCtxt,
nsIInputStream * inStr, PRUint32 sourceOffset, PRUint32 count)
{
// if we have retarged to the end stream listener, then forward the call....
// otherwise, don't do anything
nsresult rv = NS_OK;
if (m_targetStreamListener)
rv = m_targetStreamListener->OnDataAvailable(aChannel, aCtxt, inStr, sourceOffset, count);
return rv;
}
NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIChannel * aChannel, nsISupports *aCtxt,
nsresult aStatus, const PRUnichar * errorMsg)
{
nsresult rv = NS_OK;
if (m_targetStreamListener)
m_targetStreamListener->OnStopRequest(aChannel, aCtxt, aStatus, errorMsg);
m_targetStreamListener = 0;
return rv;
}
nsresult nsDocumentOpenInfo::DispatchContent(nsIChannel * aChannel, nsISupports * aCtxt)
{
nsresult rv;
nsXPIDLCString contentType;
rv = aChannel->GetContentType(getter_Copies(contentType));
if (NS_FAILED(rv)) return rv;
// go to the uri dispatcher and give them our stuff...
NS_WITH_SERVICE(nsIURILoader, pURILoader, kURILoaderCID, &rv);
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIURIContentListener> contentListener;
nsXPIDLCString desiredContentType;
//
// First step: See if any nsIURIContentListener prefers to handle this
// content type.
//
PRBool abortDispatch = PR_FALSE;
rv = pURILoader->DispatchContent(contentType, mCommand, m_windowTarget,
aChannel, aCtxt,
m_contentListener,
getter_Copies(desiredContentType),
getter_AddRefs(contentListener),
&abortDispatch);
// if the uri loader says to abort the dispatch then someone
// else must have stepped in and taken over for us...so stop..
if (abortDispatch) return NS_OK;
//
// Second step: If no listener prefers this type, see if any stream
// decoders exist to transform this content type into
// some other.
//
if (!contentListener) {
nsDocumentOpenInfo* nextLink;
// When applying stream decoders, it is necessary to "insert" an
// intermediate nsDocumentOpenInfo instance to handle the targeting of
// the "final" stream or streams.
//
// For certain content types (ie. multi-part/x-mixed-replace) the input
// stream is split up into multiple destination streams. This
// intermediate instance is used to target these "decoded" streams...
//
nextLink = Clone();
if (!nextLink) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(nextLink);
// Set up the final destination listener.
nextLink->m_targetStreamListener = nsnull;
// The following call binds this channelListener's mNextListener (typically
// the nsDocumentBindInfo) to the underlying stream converter, and returns
// the underlying stream converter which we then set to be this channelListener's
// mNextListener. This effectively nestles the stream converter down right
// in between the raw stream and the final listener.
rv = RetargetOutput(aChannel, contentType, "*/*", nextLink);
NS_RELEASE(nextLink);
if (m_targetStreamListener) {
return NS_OK;
}
}
//
// Step 3:
//
// BIG TIME HACK ALERT!!!!! WE NEED THIS HACK IN PLACE UNTIL OUR NEW UNKNOWN CONTENT
// HANDLER COMES ONLINE!!!
// Until that day, if we couldn't find a handler for the content type, then go back to the listener who
// originated the url request and force them to handle the content....this forces us through the old code
// path for unknown content types which brings up the file save as dialog...
if (!contentListener) {
contentListener = m_contentListener;
}
//
// Good news! Some content listener can handle this content type.
//
if (contentListener)
{
nsCOMPtr<nsIStreamListener> contentStreamListener;
PRBool bAbortProcess = PR_FALSE;
nsCAutoString contentTypeToUse;
if (desiredContentType)
contentTypeToUse = desiredContentType;
else
contentTypeToUse = contentType;
// We need to first figure out if we are retargeting the load to a content listener
// that is different from the one that originated the request....if so, set
// LOAD_RETARGETED_DOCUMENT_URI on the channel.
if (contentListener.get() != m_contentListener.get())
{
// we must be retargeting...so set an appropriate flag on the channel
nsLoadFlags loadAttribs = 0;
aChannel->GetLoadAttributes(&loadAttribs);
loadAttribs |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
aChannel->SetLoadAttributes(loadAttribs);
}
rv = contentListener->DoContent(contentTypeToUse, mCommand, m_windowTarget,
aChannel, getter_AddRefs(contentStreamListener),
&bAbortProcess);
// the listener is doing all the work from here...we are done!!!
if (bAbortProcess) return rv;
if (NS_FAILED(rv))
{
nsCOMPtr<nsIDOMWindow> domWindow (do_GetInterface(contentListener));
return InvokeUnknownContentHandler(aChannel, contentType, domWindow);
}
// okay, all registered listeners have had a chance to handle this content...
// did one of them give us a stream listener back? if so, let's start reading data
// into it...
rv = RetargetOutput(aChannel, contentType, desiredContentType, contentStreamListener);
}
}
return rv;
}
nsresult nsDocumentOpenInfo::InvokeUnknownContentHandler(nsIChannel * aChannel, const char * aContentType, nsIDOMWindow * aDomWindow)
{
NS_ENSURE_ARG(aChannel);
NS_ENSURE_ARG(aDomWindow);
nsCOMPtr<nsIUnknownContentTypeHandler> handler (do_GetService(NS_IUNKNOWNCONTENTTYPEHANDLER_PROGID));
NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
return handler->HandleUnknownContentType( aChannel, aContentType, aDomWindow );
}
nsresult nsDocumentOpenInfo::RetargetOutput(nsIChannel * aChannel, const char * aSrcContentType, const char * aOutContentType,
nsIStreamListener * aStreamListener)
{
nsresult rv = NS_OK;
// do we need to invoke the stream converter service?
if (aOutContentType && *aOutContentType && nsCRT::strcasecmp(aSrcContentType, aOutContentType))
{
NS_WITH_SERVICE(nsIStreamConverterService, StreamConvService, kStreamConverterServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
nsAutoString from_w (aSrcContentType);
nsAutoString to_w (aOutContentType);
// The following call binds this channelListener's mNextListener (typically
// the nsDocumentBindInfo) to the underlying stream converter, and returns
// the underlying stream converter which we then set to be this channelListener's
// mNextListener. This effectively nestles the stream converter down right
// in between the raw stream and the final listener.
rv = StreamConvService->AsyncConvertData(from_w.GetUnicode(),
to_w.GetUnicode(),
aStreamListener,
aChannel,
getter_AddRefs(m_targetStreamListener));
}
else
m_targetStreamListener = aStreamListener; // no converter necessary so use a direct pipe
return rv;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Implementation of nsURILoader
///////////////////////////////////////////////////////////////////////////////////////////////
nsURILoader::nsURILoader()
{
NS_INIT_ISUPPORTS();
m_listeners = new nsVoidArray();
}
nsURILoader::~nsURILoader()
{
if (m_listeners)
delete m_listeners;
}
NS_IMPL_ADDREF(nsURILoader);
NS_IMPL_RELEASE(nsURILoader);
NS_INTERFACE_MAP_BEGIN(nsURILoader)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader)
NS_INTERFACE_MAP_ENTRY(nsIURILoader)
NS_INTERFACE_MAP_END
NS_IMETHODIMP nsURILoader::GetStringForCommand(nsURILoadCommand aCommand, char **aStringVersion)
{
if (aCommand == nsIURILoader::viewSource)
*aStringVersion = nsCRT::strdup("view-source");
else // for now, default everything else to view normal
*aStringVersion = nsCRT::strdup("view");
return NS_OK;
}
NS_IMETHODIMP nsURILoader::RegisterContentListener(nsIURIContentListener * aContentListener)
{
nsresult rv = NS_OK;
if (m_listeners)
m_listeners->AppendElement(aContentListener);
else
rv = NS_ERROR_FAILURE;
return rv;
}
NS_IMETHODIMP nsURILoader::UnRegisterContentListener(nsIURIContentListener * aContentListener)
{
if (m_listeners)
m_listeners->RemoveElement(aContentListener);
return NS_OK;
}
NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel * aChannel,
nsURILoadCommand aCommand,
const char * aWindowTarget,
nsISupports * aWindowContext)
{
return OpenURIVia(aChannel, aCommand, aWindowTarget, aWindowContext, 0 /* ip address */);
}
NS_IMETHODIMP nsURILoader::GetTarget(const char * aWindowTarget,
nsISupports * aWindowContext,
nsISupports ** aRetargetedWindowContext)
{
nsAutoString name(aWindowTarget);
nsCOMPtr<nsIDocShellTreeItem> windowCtxtAsTreeItem (do_GetInterface(aWindowContext));
nsCOMPtr<nsIDocShellTreeItem> treeItem;
*aRetargetedWindowContext = nsnull;
if(!name.Length() || name.EqualsIgnoreCase("_self") || name.EqualsIgnoreCase("_blank"))
{
*aRetargetedWindowContext = aWindowContext;
}
else if(name.EqualsIgnoreCase("_parent"))
{
windowCtxtAsTreeItem->GetSameTypeParent(getter_AddRefs(treeItem));
if(!treeItem)
*aRetargetedWindowContext = aWindowContext;
}
else if(name.EqualsIgnoreCase("_top"))
{
windowCtxtAsTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(treeItem));
if(!treeItem)
*aRetargetedWindowContext = aWindowContext;
}
else if(name.EqualsIgnoreCase("_content"))
{
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
windowCtxtAsTreeItem->GetTreeOwner(getter_AddRefs(treeOwner));
if (treeOwner)
treeOwner->FindItemWithName(name.GetUnicode(), nsnull, getter_AddRefs(treeItem));
else
{
NS_ERROR("Someone isn't setting up the tree owner. You might like to try that."
"Things will.....you know, work.");
}
}
else
{
windowCtxtAsTreeItem->FindItemWithName(name.GetUnicode(), nsnull, getter_AddRefs(treeItem));
}
nsCOMPtr<nsISupports> treeItemCtxt (do_QueryInterface(treeItem));
if (!*aRetargetedWindowContext)
*aRetargetedWindowContext = treeItemCtxt;
NS_IF_ADDREF(*aRetargetedWindowContext);
return NS_OK;
}
NS_IMETHODIMP nsURILoader::OpenURIVia(nsIChannel * aChannel,
nsURILoadCommand aCommand,
const char * aWindowTarget,
nsISupports * aWindowContext,
PRUint32 aLocalIP)
{
// we need to create a DocumentOpenInfo object which will go ahead and open the url
// and discover the content type....
nsresult rv = NS_OK;
nsDocumentOpenInfo* loader = nsnull;
if (!aChannel) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsISupports> retargetedWindowContext;
NS_ENSURE_SUCCESS(GetTarget(aWindowTarget, aWindowContext, getter_AddRefs(retargetedWindowContext)), NS_ERROR_FAILURE);
NS_NEWXPCOM(loader, nsDocumentOpenInfo);
if (!loader) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(loader);
nsCOMPtr<nsISupports> loadCookie;
SetupLoadCookie(retargetedWindowContext, getter_AddRefs(loadCookie));
loader->Init(retargetedWindowContext); // Extra Info
// now instruct the loader to go ahead and open the url
rv = loader->Open(aChannel, aCommand, aWindowTarget, retargetedWindowContext);
NS_RELEASE(loader);
return NS_OK;
}
NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie)
{
NS_ENSURE_TRUE(aLoadCookie, NS_ERROR_INVALID_ARG);
nsCOMPtr<nsIDocumentLoader> docLoader(do_GetInterface(aLoadCookie));
NS_ENSURE_TRUE(docLoader, NS_ERROR_FAILURE);
NS_ENSURE_SUCCESS(docLoader->Stop(), NS_ERROR_FAILURE);
return NS_OK;
}
NS_IMETHODIMP
nsURILoader::GetLoadGroupForContext(nsISupports * aWindowContext, nsILoadGroup ** aLoadGroup)
{
NS_ENSURE_ARG(aWindowContext);
nsCOMPtr<nsISupports> loadCookieForWindow;
NS_ENSURE_SUCCESS(SetupLoadCookie(aWindowContext, getter_AddRefs(loadCookieForWindow)), NS_ERROR_FAILURE);
nsCOMPtr<nsIInterfaceRequestor> requestor (do_QueryInterface(loadCookieForWindow));
NS_ENSURE_TRUE(requestor, NS_ERROR_FAILURE);
return requestor->GetInterface(NS_GET_IID(nsILoadGroup), (void **) aLoadGroup);
}
NS_IMETHODIMP
nsURILoader::GetDocumentLoaderForContext(nsISupports * aWindowContext, nsIDocumentLoader ** aLoadGroup)
{
NS_ENSURE_ARG(aWindowContext);
nsCOMPtr<nsISupports> loadCookieForWindow;
NS_ENSURE_SUCCESS(SetupLoadCookie(aWindowContext, getter_AddRefs(loadCookieForWindow)), NS_ERROR_FAILURE);
nsCOMPtr<nsIInterfaceRequestor> requestor (do_QueryInterface(loadCookieForWindow));
NS_ENSURE_TRUE(requestor, NS_ERROR_FAILURE);
return requestor->GetInterface(NS_GET_IID(nsIDocumentLoader), (void **) aLoadGroup);
}
nsresult nsURILoader::SetupLoadCookie(nsISupports * aWindowContext, nsISupports ** aLoadCookie)
{
// first, see if we have already set a load cookie on the cnt listener..
// i.e. if this isn't the first time we've tried to run a url through this window
// context then we don't need to create another load cookie, we can reuse the first one.
nsresult rv = NS_OK;
nsCOMPtr<nsISupports> loadCookie;
nsCOMPtr<nsIURIContentListener> cntListener (do_GetInterface(aWindowContext));
if (cntListener)
{
rv = cntListener->GetLoadCookie(getter_AddRefs(loadCookie));
if (NS_FAILED(rv) || !loadCookie)
{
// if we don't have a load cookie for this window context yet, then
// go create one! In order to create a load cookie, we need to get
// the parent's load cookie if there is one...
nsCOMPtr<nsIURIContentListener> parentListener;
cntListener->GetParentContentListener(getter_AddRefs(parentListener));
if (parentListener)
rv = parentListener->GetLoadCookie(getter_AddRefs(loadCookie));
nsLoadCookie * newLoadCookie = new nsLoadCookie();
if (newLoadCookie)
{
newLoadCookie->Init(loadCookie);
rv = cntListener->SetLoadCookie (NS_STATIC_CAST(nsISupports *, (nsIInterfaceRequestor *) newLoadCookie));
newLoadCookie->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(loadCookie));
} // if we created a new cookie
} // if we don't have a load cookie already
} // if we have a cntListener
*aLoadCookie = loadCookie;
NS_IF_ADDREF(*aLoadCookie);
return rv;
}
PRBool nsURILoader::ShouldHandleContent(nsIURIContentListener * aCntListener,
const char * aContentType,
nsURILoadCommand aCommand,
const char * aWindowTarget,
char ** aContentTypeToUse)
{
PRBool foundContentHandler = PR_FALSE;
if (aCommand == nsIURILoader::viewUserClick)
aCntListener->IsPreferred(aContentType, aCommand, aWindowTarget,
aContentTypeToUse,
&foundContentHandler);
else
aCntListener->CanHandleContent(aContentType, aCommand, aWindowTarget,
aContentTypeToUse,
&foundContentHandler);
return foundContentHandler;
}
NS_IMETHODIMP nsURILoader::DispatchContent(const char * aContentType,
nsURILoadCommand aCommand,
const char * aWindowTarget,
nsIChannel * aChannel,
nsISupports * aCtxt,
nsIURIContentListener * aContentListener,
char ** aContentTypeToUse,
nsIURIContentListener ** aContentListenerToUse,
PRBool * aAbortProcess)
{
NS_ENSURE_ARG(aContentType);
NS_ENSURE_ARG(aChannel);
// okay, now we've discovered the content type. We need to do the following:
// (1) We always start with the original content listener (if any) that originated the request
// and then ask if it can handle the content.
// (2) if it can't, we'll move on to the registered content listeners and give
// them a crack at handling the content.
// (3) if we cannot find a registered content lister to handle the type, then we move on to
// phase II which is to try to find a content handler in the registry for the content type.
// hitting this phase usually means we'll be creating a new window or handing off to an
// external application.
nsresult rv = NS_OK;
nsCOMPtr<nsIURIContentListener> listenerToUse = aContentListener;
PRBool skipRetargetingSearch = PR_FALSE;
// How do we determine whether we need to ask any registered content listeners if they
// want a crack at the content?
// (1) if the window target is blank or new, then we don't want to ask...
if (!nsCRT::strcasecmp(aWindowTarget, "_blank") || !nsCRT::strcasecmp(aWindowTarget, "_new"))
skipRetargetingSearch = PR_TRUE;
else
{
// (2) if the original content listener is NULL and we have a target name then we
// must not be a window open with that target name so skip the content listener search
// and skip to the part that brings up the new window.
if (aWindowTarget && *aWindowTarget && !aContentListener)
skipRetargetingSearch = PR_TRUE;
}
// find a content handler that can and will handle the content
if (!skipRetargetingSearch)
{
PRBool foundContentHandler = PR_FALSE;
if (listenerToUse)
foundContentHandler = ShouldHandleContent(listenerToUse, aContentType,
aCommand, aWindowTarget, aContentTypeToUse);
if (!foundContentHandler) // if it can't handle the content, scan through the list of registered listeners
{
PRInt32 i = 0;
// keep looping until we get a content listener back
for(i = 0; i < m_listeners->Count() && !foundContentHandler; i++)
{
//nsIURIContentListener's aren't refcounted.
nsIURIContentListener * listener =(nsIURIContentListener*)m_listeners->ElementAt(i);
if (listener)
{
foundContentHandler = ShouldHandleContent(listener, aContentType,
aCommand, aWindowTarget, aContentTypeToUse);
if (foundContentHandler)
listenerToUse = listener;
}
} // for loop
} // if we can't handle the content
if (foundContentHandler && listenerToUse)
{
*aContentListenerToUse = listenerToUse;
NS_IF_ADDREF(*aContentListenerToUse);
return rv;
}
}
// no registered content listeners to handle this type!!! so go to the register
// and get a registered nsIContentHandler for our content type. Hand it off
// to them...
// eventually we want to hit up the category manager so we can allow people to
// over ride the default content type handlers....for now...i'm skipping that part.
nsCAutoString handlerProgID (NS_CONTENT_HANDLER_PROGID_PREFIX);
handlerProgID += aContentType;
nsCOMPtr<nsIContentHandler> aContentHandler;
rv = nsComponentManager::CreateInstance(handlerProgID, nsnull, NS_GET_IID(nsIContentHandler), getter_AddRefs(aContentHandler));
if (NS_SUCCEEDED(rv)) // we did indeed have a content handler for this type!! yippee...
{
rv = aContentHandler->HandleContent(aContentType, "view", aWindowTarget, aChannel);
*aAbortProcess = PR_TRUE;
}
return rv;
}