309525 add a method to nsIURILoader that accepts a (possibly already opened)

channel and returns a streamlistener for getting the data. Add a flag
DONT_RETARGET for ensuring that content will load in a specific docshell or not
at all.

Also, fix nsChromeProtocolHandler to correctly implement IsPending.

r=bz sr=darin
This commit is contained in:
cbiesinger%web.de 2005-12-17 18:26:25 +00:00
Родитель c998b667ae
Коммит 4fd815bd39
3 изменённых файлов: 237 добавлений и 112 удалений

Просмотреть файл

@ -146,7 +146,7 @@ public:
// nsIRequest
NS_IMETHOD GetName(nsACString &result) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD IsPending(PRBool *_retval) { *_retval = PR_TRUE; return NS_OK; }
NS_IMETHOD IsPending(PRBool *_retval) { *_retval = (mListener != nsnull); return NS_OK; }
NS_IMETHOD GetStatus(nsresult *status) { *status = mStatus; return NS_OK; }
NS_IMETHOD Cancel(nsresult status) { mStatus = status; return NS_OK; }
NS_IMETHOD Suspend(void) { return NS_OK; }

Просмотреть файл

@ -67,9 +67,26 @@ interface nsIInterfaceRequestor;
* or helper app. Or it may hand the url off to an OS registered
* application.
*/
[scriptable, uuid(5cf6420c-74f3-4a7c-bc1d-f5756d79ea07)]
[scriptable, uuid(2f7e8051-f1c9-4bcc-8584-9cfd5849e343)]
interface nsIURILoader : nsISupports
{
/**
* @name Flags for opening URIs.
*/
/* @{ */
/**
* Should the content be displayed in a container that prefers the
* content-type, or will any container do.
*/
const unsigned long IS_CONTENT_PREFERRED = 1 << 0;
/**
* If this flag is set, only the listener of the specified window context will
* be considered for content handling; if it refuses the load, an error will
* be indicated.
*/
const unsigned long DONT_RETARGET = 1 << 1;
/* @} */
/**
* As applications such as messenger and the browser are instantiated,
* they register content listener's with the uri dispatcher corresponding
@ -106,6 +123,44 @@ interface nsIURILoader : nsISupports
in boolean aIsContentPreferred,
in nsIInterfaceRequestor aWindowContext);
/**
* Loads data from a channel. This differs from openURI in that the channel
* may already be opened, and that it returns a stream listener into which the
* caller should pump data. The caller is responsible for opening the channel
* and pumping the channel's data into the returned stream listener.
*
* Note: If the channel already has a loadgroup, it will be replaced with the
* window context's load group, or null if the context doesn't have one.
*
* If the window context's nsIURIContentListener refuses the load immediately
* (e.g. in nsIURIContentListener::onStartURIOpen), this method will return
* NS_ERROR_WONT_HANDLE_CONTENT. At that point, the caller should probably
* cancel the channel if it's already open (this method will not cancel the
* channel).
*
* If flags include DONT_RETARGET, and the content listener refuses the load
* during onStartRequest (e.g. in canHandleContent/isPreferred), then the
* returned stream listener's onStartRequest method will return
* NS_ERROR_WONT_HANDLE_CONTENT.
*
* @param aChannel
* The channel that should be loaded. The channel may already be
* opened. It must not be closed (i.e. this must be called before the
* channel calls onStopRequest on its stream listener).
* @param aFlags
* Combination (bitwise OR) of the flags specified above. 0 indicates
* default handling.
* @param aWindowContext
* If you are running the url from a doc shell or a web shell, this is
* your window context. If you have a content listener you want to
* give first crack to, the uri loader needs to be able to get it
* from the window context. We will also be using the window context
* to get at the progress event sink interface.
* <b>Must not be null!</b>
*/
nsIStreamListener openChannel(in nsIChannel aChannel,
in unsigned long aFlags,
in nsIInterfaceRequestor aWindowContext);
/**
* Stops an in progress load

Просмотреть файл

@ -101,13 +101,19 @@ public:
nsDocumentOpenInfo();
// Real constructor
// aFlags is a combination of the flags on nsIURILoader
nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
PRBool aIsContentPreferred,
PRUint32 aFlags,
nsURILoader* aURILoader);
NS_DECL_ISUPPORTS
nsresult Open(nsIChannel* channel);
/**
* Prepares this object for receiving data. The stream
* listener methods of this class must not be called before calling this
* method.
*/
nsresult Prepare();
// Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
// take the data off our hands.
@ -159,10 +165,12 @@ protected:
nsCOMPtr<nsIInterfaceRequestor> m_originalContext;
/**
* Boolean to pass to CanHandleContent (also determines whether we
* use CanHandleContent or IsPreferred).
* IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent
* (also determines whether we use CanHandleContent or IsPreferred).
* DONT_RETARGET means that we will only try m_originalContext, no other
* listeners.
*/
PRBool mIsContentPreferred;
PRUint32 mFlags;
/**
* The type of the data we will be trying to dispatch.
@ -191,10 +199,10 @@ nsDocumentOpenInfo::nsDocumentOpenInfo()
}
nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
PRBool aIsContentPreferred,
PRUint32 aFlags,
nsURILoader* aURILoader)
: m_originalContext(aWindowContext),
mIsContentPreferred(aIsContentPreferred),
mFlags(aFlags),
mURILoader(aURILoader)
{
}
@ -203,33 +211,14 @@ nsDocumentOpenInfo::~nsDocumentOpenInfo()
{
}
nsresult nsDocumentOpenInfo::Open(nsIChannel *aChannel)
nsresult nsDocumentOpenInfo::Prepare()
{
LOG(("[0x%p] nsDocumentOpenInfo::Open", this));
// 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....
LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
nsresult rv;
// ask our window context if it has a uri content listener...
m_contentListener = do_GetInterface(m_originalContext, &rv);
if (NS_FAILED(rv)) return rv;
// now just open the channel!
rv = aChannel->AsyncOpen(this, nsnull);
// no content from this load - that's OK.
if (rv == NS_ERROR_DOM_RETVAL_UNDEFINED ||
rv == NS_ERROR_NO_CONTENT) {
LOG((" rv is NS_ERROR_DOM_RETVAL_UNDEFINED or NS_ERROR_NO_CONTENT -- doing nothing"));
rv = NS_OK;
}
return rv;
}
@ -503,82 +492,91 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports *
return NS_OK;
}
//
// Second step: See whether some other registered listener wants
// to handle this content type.
//
PRInt32 count = mURILoader->m_listeners.Count();
nsCOMPtr<nsIURIContentListener> listener;
for (PRInt32 i = 0; i < count; i++) {
listener = do_QueryReferent(mURILoader->m_listeners[i]);
if (listener) {
if (TryContentListener(listener, aChannel)) {
LOG((" Found listener registered on the URILoader"));
return NS_OK;
}
} else {
// remove from the listener list, reset i and update count
mURILoader->m_listeners.RemoveObjectAt(i--);
--count;
}
}
// If we aren't allowed to try other listeners, we're done here.
if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
//
// Third step: Try to find a content listener that has not yet had
// the chance to register, as it is contained in a not-yet-loaded
// module, but which has registered a contract ID.
//
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (catman) {
nsXPIDLCString contractidString;
rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
mContentType.get(),
getter_Copies(contractidString));
if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
LOG((" Listener contractid for '%s' is '%s'",
mContentType.get(), contractidString.get()));
listener = do_CreateInstance(contractidString);
LOG((" Listener from category manager: 0x%p", listener.get()));
if (listener && TryContentListener(listener, aChannel)) {
LOG((" Listener from category manager likes this type"));
return NS_OK;
//
// Second step: See whether some other registered listener wants
// to handle this content type.
//
PRInt32 count = mURILoader->m_listeners.Count();
nsCOMPtr<nsIURIContentListener> listener;
for (PRInt32 i = 0; i < count; i++) {
listener = do_QueryReferent(mURILoader->m_listeners[i]);
if (listener) {
if (TryContentListener(listener, aChannel)) {
LOG((" Found listener registered on the URILoader"));
return NS_OK;
}
} else {
// remove from the listener list, reset i and update count
mURILoader->m_listeners.RemoveObjectAt(i--);
--count;
}
}
}
//
// Fourth step: try to find an nsIContentHandler for our type.
//
nsCAutoString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
handlerContractID += mContentType;
//
// Third step: Try to find a content listener that has not yet had
// the chance to register, as it is contained in a not-yet-loaded
// module, but which has registered a contract ID.
//
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (catman) {
nsXPIDLCString contractidString;
rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
mContentType.get(),
getter_Copies(contractidString));
if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
LOG((" Listener contractid for '%s' is '%s'",
mContentType.get(), contractidString.get()));
nsCOMPtr<nsIContentHandler> contentHandler =
do_CreateInstance(handlerContractID.get());
if (contentHandler) {
LOG((" Content handler found"));
rv = contentHandler->HandleContent(mContentType.get(),
m_originalContext, request);
// XXXbz returning an error code to represent handling the
// content is just bizarre!
if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
if (NS_FAILED(rv)) {
// The content handler has unexpectedly failed. Cancel the request
// just in case the handler didn't...
LOG((" Content handler failed. Aborting load"));
request->Cancel(rv);
listener = do_CreateInstance(contractidString);
LOG((" Listener from category manager: 0x%p", listener.get()));
if (listener && TryContentListener(listener, aChannel)) {
LOG((" Listener from category manager likes this type"));
return NS_OK;
}
}
}
//
// Fourth step: try to find an nsIContentHandler for our type.
//
nsCAutoString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
handlerContractID += mContentType;
nsCOMPtr<nsIContentHandler> contentHandler =
do_CreateInstance(handlerContractID.get());
if (contentHandler) {
LOG((" Content handler found"));
rv = contentHandler->HandleContent(mContentType.get(),
m_originalContext, request);
// XXXbz returning an error code to represent handling the
// content is just bizarre!
if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
if (NS_FAILED(rv)) {
// The content handler has unexpectedly failed. Cancel the request
// just in case the handler didn't...
LOG((" Content handler failed. Aborting load"));
request->Cancel(rv);
}
#ifdef PR_LOGGING
else {
LOG((" Content handler taking over load"));
}
else {
LOG((" Content handler taking over load"));
}
#endif
return rv;
return rv;
}
}
}
} else if (mFlags & nsIURILoader::DONT_RETARGET) {
// External handling was forced, but we must not retarget
// -> abort
LOG((" External handling forced, but not allowed to retarget -> aborting"));
return NS_ERROR_WONT_HANDLE_CONTENT;
}
NS_ASSERTION(!m_targetStreamListener,
@ -608,7 +606,12 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports *
return NS_OK;
}
}
if (mFlags & nsIURILoader::DONT_RETARGET) {
LOG((" Listener not interested and no stream converter exists, and retargeting disallowed -> aborting"));
return NS_ERROR_WONT_HANDLE_CONTENT;
}
// Sixth step:
//
// All attempts to dispatch this content have failed. Just pass it off to
@ -675,7 +678,7 @@ nsDocumentOpenInfo::ConvertData(nsIRequest *request,
// intermediate instance is used to target these "decoded" streams...
//
nsCOMPtr<nsDocumentOpenInfo> nextLink =
new nsDocumentOpenInfo(m_originalContext, mIsContentPreferred, mURILoader);
new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
if (!nextLink) return NS_ERROR_OUT_OF_MEMORY;
LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
@ -708,8 +711,8 @@ PRBool
nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
nsIChannel* aChannel)
{
LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mIsContentPreferred = %s",
this, mIsContentPreferred ? "true" : "false"));
LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x",
this, mFlags));
NS_PRECONDITION(aListener, "Must have a non-null listener");
NS_PRECONDITION(aChannel, "Must have a channel");
@ -717,7 +720,7 @@ nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
PRBool listenerWantsContent = PR_FALSE;
nsXPIDLCString typeToUse;
if (mIsContentPreferred) {
if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) {
aListener->IsPreferred(mContentType.get(),
getter_Copies(typeToUse),
&listenerWantsContent);
@ -767,8 +770,9 @@ nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
aChannel->SetLoadFlags(loadFlags | newLoadFlags);
PRBool abort = PR_FALSE;
PRBool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
nsresult rv = aListener->DoContent(mContentType.get(),
mIsContentPreferred,
isPreferred,
aChannel,
getter_AddRefs(m_targetStreamListener),
&abort);
@ -860,7 +864,53 @@ NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
LOG(("nsURILoader::OpenURI for %s", spec.get()));
}
#endif
nsCOMPtr<nsIStreamListener> loader;
nsresult rv = OpenChannel(channel,
aIsContentPreferred ? IS_CONTENT_PREFERRED : 0,
aWindowContext,
getter_AddRefs(loader));
if (NS_SUCCEEDED(rv)) {
// 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....
rv = channel->AsyncOpen(loader, nsnull);
// no content from this load - that's OK.
if (rv == NS_ERROR_DOM_RETVAL_UNDEFINED ||
rv == NS_ERROR_NO_CONTENT) {
LOG((" rv is NS_ERROR_DOM_RETVAL_UNDEFINED or NS_ERROR_NO_CONTENT -- doing nothing"));
rv = NS_OK;
}
} else if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {
// Not really an error, from this method's point of view
rv = NS_OK;
}
return rv;
}
NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel,
PRUint32 aFlags,
nsIInterfaceRequestor* aWindowContext,
nsIStreamListener** aListener)
{
NS_ASSERTION(channel, "Trying to open a null channel!");
NS_ASSERTION(aWindowContext, "Window context must not be null");
#ifdef PR_LOGGING
if (LOG_ENABLED()) {
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
nsCAutoString spec;
uri->GetAsciiSpec(spec);
LOG(("nsURILoader::OpenChannel for %s", spec.get()));
}
#endif
// 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));
@ -870,18 +920,18 @@ NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
if (uri) {
PRBool doAbort = PR_FALSE;
winContextListener->OnStartURIOpen(uri, &doAbort);
if (doAbort) {
LOG((" OnStartURIOpen aborted load"));
return NS_OK;
return NS_ERROR_WONT_HANDLE_CONTENT;
}
}
}
}
// we need to create a DocumentOpenInfo object which will go ahead and open
// the url and discover the content type....
nsCOMPtr<nsDocumentOpenInfo> loader =
new nsDocumentOpenInfo(aWindowContext, aIsContentPreferred, this);
new nsDocumentOpenInfo(aWindowContext, aFlags, this);
if (!loader) return NS_ERROR_OUT_OF_MEMORY;
@ -891,7 +941,7 @@ NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
if (!loadGroup) {
// XXXbz This context is violating what we'd like to be the new uriloader
// api.... Set up a nsDocLoader to handle the loadgroup for this context.
// This really needs to go away!
// This really needs to go away!
nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext));
if (listener) {
nsCOMPtr<nsISupports> cookie;
@ -907,12 +957,32 @@ NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
listener->SetLoadCookie(nsDocLoader::GetAsSupports(newDocLoader));
}
}
}
}
// If the channel is pending, then we need to remove it from its current
// loadgroup
PRBool pending;
if (NS_SUCCEEDED(channel->IsPending(&pending)) && pending) {
nsCOMPtr<nsILoadGroup> oldGroup;
channel->GetLoadGroup(getter_AddRefs(oldGroup));
if (oldGroup) {
oldGroup->RemoveRequest(channel, nsnull, NS_BINDING_RETARGETED);
}
} else {
pending = PR_FALSE;
}
channel->SetLoadGroup(loadGroup);
// now instruct the loader to go ahead and open the url
return loader->Open(channel);
if (pending) {
loadGroup->AddRequest(channel, nsnull);
}
// prepare the loader for receiving data
nsresult rv = loader->Prepare();
if (NS_SUCCEEDED(rv))
NS_ADDREF(*aListener = loader);
return rv;
}
NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie)