This document describes the beginning of the document loading process. We start with the request to load a particular link in a particular window, and proceed up to the point at which the data stream is dispatched to the proper handler. The final goal is to find the correct stream listener to pump the data into when necko calls OnDataAvailable (e.g., we may find the HTML parser as the stream listener to give the data to).
Dramatis Personae
This document focuses on the interaction of three classes with each other, but other Mozilla components are also involved.
Main Roles
nsDocShell
- This class corresponds, basically, to a "window" object in JavaScript -- each frame, iframe, content area, tab, etc has its own docshell. A docshell can have a name associated with it (e.g. a named frame). All loads that we consider in this document are initiated via the docshell.
nsDocShell
implements too many interfaces to count; the ones that matter to us most arensIWebNavigation
andnsILinkHandler
. nsURILoader
- This is a service implementing
nsIURILoader
. It keeps track of currently pending loads and registered content listeners. Some facilities are provided for starting loads, canceling loads, and other such micromanagement. nsDocumentOpenInfo
- This class encapsulates all the information related to a particular load. There is basically one of these per load (though see the section on stream converters).
nsDocumentOpenInfo
implementsnsIStreamListener
and proxies thensIStreamListener
calls over to the "real" stream listener once one is chosen. This is the class that handles deciding where the data from the load should go.
Supporting Roles
- Necko
- This is the Mozilla networking library. This handles actually going out and getting the data. It also handles determining the MIME type of the data, which is used to decide who gets the data.
- Category Manager
- This is used in a last-ditch attempt to find a content listener.
nsIURIContentListener
implementations- We try to find one of these which can handle data of the type we're looking at (that is, it can give us an
nsIStreamListener
to pump data into). Most often, the one we find isnsDSURIContentListener
, which corresponds to a docshell and handles most of the data types that Mozilla handles internally. nsIExternalHelperAppService
- If we can't do anything else with a load, we give it to the
nsIExternalHelperAppService
and let it look for a helper app, put up the "what do I do now?" dialog, and so forth.
Bird's Eye View
<map id="loadDiagramMap" name="loadDiagramMap"><area alt="(13) DoContent()" coords="534,239,715,300" href="#nsIExternalHelperAppService::DoContent"> <area alt="(10) GetCategoryEntry()" coords="575,418,821,418,821,455,629,455,629,484,575,484" href="#CategoryManager" shape="poly"> <area alt="(12)" coords="539,133,583,163" href="#stream-converter"> <area alt="(11)" coords="485,133,537,163" href="#ContentHandler"> <area alt="(9)" coords="445,132,484,165" href="#nsDocumentOpenInfo::DispatchContent"> <area alt="(8)" coords="405,133,439,162" href="#OnStartRequest-innards"> <area alt="(7) OnStartRequest()" coords="639,129,703,129,703,165,833,165,833,204,639,204" href="#OnStartRequest" shape="poly"> <area alt="(6) AsyncOpen()" coords="637,121,709,121,709,96,783,96,783,58,637,58" href="#AsyncOpen" shape="poly"> <area alt="(5) Open()" coords="311,306,432,371" href="#Open"> <area alt="(4)" coords="90,384,127,417" href="#openURI-innards"> <area alt="(0) RegisterContentListener()" coords="37,474,346,474,346,505,88,505,88,535,37,535" href="#RegisterContentListener" shape="poly"> <area alt="(3) openURI() (nsURILoader)" coords="5,207,312,269" href="#openURI"> <area alt="(2)" coords="102,114,139,148" href="#InternalLoad"> <area alt="(1) loadURI/onLinkClick" coords="77,5,449,59" href="#loadURI"> <area alt="nsIExternalHelperAppService" coords="527,305,839,339" href="#nsIExternalHelperAppService"> <area alt="Category Manager" coords="683,467,807,527" href="#nsCategoryManager"> <area alt="nsDocumentOpenInfo" coords="371,71,635,185" href="#nsDocumentOpenInfo"> <area alt="Necko" coords="721,113,821,157" href="#necko"> <area alt="nsURILoader" coords="23,335,215,455" href="#nsURILoader"> <area coords="227,515,485,575" href="#nsIURIContentListener"> <area alt="nsDocShell" coords="47,83,203,153" href="#nsDocShell"></map>
nsURILoader
. The prose description below has been updated. The changes do not affect functionality and the step numbers in the diagram are still accurate.Guided Tour
-
During startup and component initialization, components register themselves with the URILoader via
RegisterContentListener
. These registered listeners are used later during content dispatch. -
API calls to load a new URI. These can come in via
nsIWebNavigation
(a scriptable embedding interface) ornsILinkHandler
(an internal interface used for link clicks). Both interfaces are implemented bynsWebShell/nsDocShell
. These API calls will typically pass in a URI string or object to load, and may include information like the name of the target frame (for<a target="something">
, e.g.). -
nsDocShell::InternalLoad()
handles targeting to the correct docshell (if the load has a target window associated with it), scrolling to anchors (if the load is an anchor within the current page), and session history issues. It callsDoURILoad()
.DoURILoad()
creates a channel, massages it to have the right POST data, load flags, cache key, referrer, etc. It then passes the channel toDoChannelLoad
, which does some more flag massaging and then calls into the URILoader. -
nsURILoader::OpenURI
gets a channel to open, a boolean indicating whether this load is the result of a link click, and annsISupports
"window context" (the docshell triggering the load, actually, but in drag and heavy makeup). It immediately hands off tonsURILoader::OpenChannel
. -
nsURILoader::OpenChannel
notifies thensIURIContentListener
hanging off the window context, if any, of the start of the load; this gives embedders a chance to abort the load if this URI type is something they want to handle in the embedding app. If the load is not aborted, we create annsDocumentOpenInfo
object for this load, passing it the "this is a link click" boolean and the window context. We then set the channel'snsILoadGroup
to the one from the window context. -
nsDocumentOpenInfo::Prepare
is passed the channel to open. It callsGetInterface
on the window context to get and save that context'snsIURIContentListener
. -
OpenChannel
returns toOpenURI
which just opens the channel, setting thensDocumentOpenInfo
as the stream listener. -
OnStartRequest
notification comes back from Necko -
nsDocumentOpenInfo::OnStartRequest
checks the response status code on the channel for HTTP channels and drops the load on the floor if warranted (e.g. 204 or 205 responses). Then it callsnsDocumentOpenInfo::DispatchContent
-
nsDocumentOpenInfo::DispatchContent
starts doing the real dirty work. First it checks whether the channel has "Content-Disposition: attachment" set. If so, it skips trying to find an internal viewer and goes right over to looking for a stream converter. Otherwise,DispatchContent
goes through a three-step process to try to find the correct listener.The basic idea is that
DispatchContent
callsnsDocumentOpenInfo::TryContentListener
on various content listeners; if that returns true, we have found the right listener and we are done.TryContentListener
makes use of theIsPreferred
andCanHandleContent
functions onnsIURIContentListener
and callsDoContent()
on the listener if it claims to handle the data, as well as hooking up a stream converter if the listener asks for one. If any of those steps fails, the function returns false so that another content listener will be looked for.The first content listener we check is the content listener associated with our window context (the docshell that initiated the load). If this can't handle the content type, we loop over our stored list of possible listeners (previously registered via
RegisterContentListener
) and ask each one in turn whether it can handle the data. -
If we still have not found an
nsIURIContentListener
, we ask the category manager whether it has an entry for the desired content type under theNS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY
key. If it does, we instantiate that component using its contractid (viaCreateInstance
) and callTryContentListener
on it. -
If we find no content listener willing to handle the data, we look for a content handler (using
CreateInstance
onNS_CONTENT_HANDLER_CONTRACTID_PREFIX + aContentType
and expecting back annsIContentHandler
). If a handler is found, we callHandleContent
on it to give it a chance to take over the load. If it does so, we abort the entire dispatch process right here and cancel the necko request; we will not be getting a stream listener to stream data into, since the content handler has taken over completely. -
If at this point we do not have an
nsIURIContentListener
, we have failed to find a way to handle this content type. If that is the case,DispatchContent
tries to convert the data to some other content type by looking for a stream converter (implementingnsIStreamConverter
) that takes our content type as input and outputs the magic type "*/*" (which just means we'll take anything it can give us). If the type of the data is "application/x-unknown-content-type" (another magic type), this is where nsUnknownDecoder would be instantiated.The conversion attempt is made by calling
ConvertData
. This creates a newnsDocumentOpenInfo
object and sets it as the output streamlistener of the converter. This way, once we know what type we've managed to get we can attempt to redispatch it.If a converter is found, we hook that up as our stream listener and are done -- we just need to pump data into it and let the downstream
nsDocumentOpenInfo
handle the final dispatch. -
If we still do not have a stream listener, that means that we failed to find an
nsIURIContentListener
ornsIContentHandler
for this type. Give the load to the helper app service; this will return annsIStreamListener
that we can use.
Original Document Information
- Author(s): Boris Zbarsky
- Last Updated Date: October 24, 2003
- Copyright Information: Portions of this content are © 1998–2007 by individual mozilla.org contributors; content available under a Creative Commons license | Details.