1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1999
21 * the Initial Developer. All Rights Reserved.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsURILoader.h"
40 #include "nsAutoPtr.h"
41 #include "nsIURIContentListener.h"
42 #include "nsIContentHandler.h"
43 #include "nsILoadGroup.h"
44 #include "nsIDocumentLoader.h"
45 #include "nsIWebProgress.h"
46 #include "nsIWebProgressListener.h"
47 #include "nsIIOService.h"
48 #include "nsIServiceManager.h"
49 #include "nsIStreamListener.h"
51 #include "nsIChannel.h"
52 #include "nsIInterfaceRequestor.h"
53 #include "nsIInterfaceRequestorUtils.h"
54 #include "nsIProgressEventSink.h"
55 #include "nsIInputStream.h"
56 #include "nsIStreamConverterService.h"
57 #include "nsWeakReference.h"
58 #include "nsIHttpChannel.h"
59 #include "nsIMultiPartChannel.h"
62 #include "nsIDocShell.h"
63 #include "nsIDocShellTreeItem.h"
64 #include "nsIDocShellTreeOwner.h"
66 #include "nsXPIDLString.h"
68 #include "nsNetUtil.h"
69 #include "nsIDOMWindowInternal.h"
70 #include "nsReadableUtils.h"
71 #include "nsDOMError.h"
73 #include "nsICategoryManager.h"
74 #include "nsCExternalHandlerService.h" // contains contractids for the helper app service
76 #include "nsIMIMEHeaderParam.h"
79 #include "nsMimeTypes.h"
81 #include "nsDocLoader.h"
83 #include "mozilla/FunctionTimer.h"
84 #ifdef NS_FUNCTION_TIMER
85 #define TIME_URILOADER_FUNCTION(req) \
86 nsCAutoString name__("N/A"); \
87 (req)->GetName(name__); \
88 NS_TIME_FUNCTION_FMT("%s (line %d) (request: %s)", \
93 #define TIME_URILOADER_FUNCTION(req) do {} while(0)
97 PRLogModuleInfo
* nsURILoader::mLog
= nsnull
;
100 #define LOG(args) PR_LOG(nsURILoader::mLog, PR_LOG_DEBUG, args)
101 #define LOG_ERROR(args) PR_LOG(nsURILoader::mLog, PR_LOG_ERROR, args)
102 #define LOG_ENABLED() PR_LOG_TEST(nsURILoader::mLog, PR_LOG_DEBUG)
105 * The nsDocumentOpenInfo contains the state required when a single
106 * document is being opened in order to discover the content type...
107 * Each instance remains alive until its target URL has been loaded
110 class nsDocumentOpenInfo
: public nsIStreamListener
113 // Needed for nsCOMPtr to work right... Don't call this!
114 nsDocumentOpenInfo();
117 // aFlags is a combination of the flags on nsIURILoader
118 nsDocumentOpenInfo(nsIInterfaceRequestor
* aWindowContext
,
120 nsURILoader
* aURILoader
);
125 * Prepares this object for receiving data. The stream
126 * listener methods of this class must not be called before calling this
131 // Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
132 // take the data off our hands.
133 nsresult
DispatchContent(nsIRequest
*request
, nsISupports
* aCtxt
);
135 // Call this if we need to insert a stream converter from aSrcContentType to
136 // aOutContentType into the StreamListener chain. DO NOT call it if the two
137 // types are the same, since no conversion is needed in that case.
138 nsresult
ConvertData(nsIRequest
*request
,
139 nsIURIContentListener
*aListener
,
140 const nsACString
& aSrcContentType
,
141 const nsACString
& aOutContentType
);
144 * Function to attempt to use aListener to handle the load. If
145 * PR_TRUE is returned, nothing else needs to be done; if PR_FALSE
146 * is returned, then a different way of handling the load should be
149 PRBool
TryContentListener(nsIURIContentListener
* aListener
,
150 nsIChannel
* aChannel
);
152 // nsIRequestObserver methods:
153 NS_DECL_NSIREQUESTOBSERVER
155 // nsIStreamListener methods:
156 NS_DECL_NSISTREAMLISTENER
159 ~nsDocumentOpenInfo();
163 * The first content listener to try dispatching data to. Typically
164 * the listener associated with the entity that originated the load.
166 nsCOMPtr
<nsIURIContentListener
> m_contentListener
;
169 * The stream listener to forward nsIStreamListener notifications
170 * to. This is set once the load is dispatched.
172 nsCOMPtr
<nsIStreamListener
> m_targetStreamListener
;
175 * A pointer to the entity that originated the load. We depend on getting
176 * things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
178 nsCOMPtr
<nsIInterfaceRequestor
> m_originalContext
;
181 * IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent
182 * (also determines whether we use CanHandleContent or IsPreferred).
183 * DONT_RETARGET means that we will only try m_originalContext, no other
189 * The type of the data we will be trying to dispatch.
191 nsCString mContentType
;
194 * Reference to the URILoader service so we can access its list of
195 * nsIURIContentListeners.
197 nsRefPtr
<nsURILoader
> mURILoader
;
200 NS_IMPL_THREADSAFE_ADDREF(nsDocumentOpenInfo
)
201 NS_IMPL_THREADSAFE_RELEASE(nsDocumentOpenInfo
)
203 NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo
)
204 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIRequestObserver
)
205 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
206 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
207 NS_INTERFACE_MAP_END_THREADSAFE
209 nsDocumentOpenInfo::nsDocumentOpenInfo()
211 NS_NOTREACHED("This should never be called\n");
214 nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor
* aWindowContext
,
216 nsURILoader
* aURILoader
)
217 : m_originalContext(aWindowContext
),
219 mURILoader(aURILoader
)
223 nsDocumentOpenInfo::~nsDocumentOpenInfo()
227 nsresult
nsDocumentOpenInfo::Prepare()
229 LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
233 // ask our window context if it has a uri content listener...
234 m_contentListener
= do_GetInterface(m_originalContext
, &rv
);
238 NS_IMETHODIMP
nsDocumentOpenInfo::OnStartRequest(nsIRequest
*request
, nsISupports
* aCtxt
)
240 TIME_URILOADER_FUNCTION(request
);
242 LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
247 // Deal with "special" HTTP responses:
249 // - In the case of a 204 (No Content) or 205 (Reset Content) response, do
250 // not try to find a content handler. Return NS_BINDING_ABORTED to cancel
251 // the request. This has the effect of ensuring that the DocLoader does
252 // not try to interpret this as a real request.
254 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(request
, &rv
));
256 if (NS_SUCCEEDED(rv
)) {
257 PRUint32 responseCode
= 0;
259 rv
= httpChannel
->GetResponseStatus(&responseCode
);
262 LOG_ERROR((" Failed to get HTTP response status"));
264 // behave as in the canceled case
268 LOG((" HTTP response status: %d", responseCode
));
270 if (204 == responseCode
|| 205 == responseCode
) {
271 return NS_BINDING_ABORTED
;
276 // Make sure that the transaction has succeeded, so far...
280 rv
= request
->GetStatus(&status
);
282 NS_ASSERTION(NS_SUCCEEDED(rv
), "Unable to get request status!");
283 if (NS_FAILED(rv
)) return rv
;
285 if (NS_FAILED(status
)) {
286 LOG_ERROR((" Request failed, status: 0x%08X", rv
));
289 // The transaction has already reported an error - so it will be torn
290 // down. Therefore, it is not necessary to return an error code...
295 rv
= DispatchContent(request
, aCtxt
);
297 LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08X", m_targetStreamListener
.get(), rv
));
299 NS_ASSERTION(NS_SUCCEEDED(rv
) || !m_targetStreamListener
,
300 "Must not have an m_targetStreamListener with a failure return!");
302 NS_ENSURE_SUCCESS(rv
, rv
);
304 if (m_targetStreamListener
)
305 rv
= m_targetStreamListener
->OnStartRequest(request
, aCtxt
);
307 LOG((" OnStartRequest returning: 0x%08X", rv
));
312 NS_IMETHODIMP
nsDocumentOpenInfo::OnDataAvailable(nsIRequest
*request
, nsISupports
* aCtxt
,
313 nsIInputStream
* inStr
, PRUint32 sourceOffset
, PRUint32 count
)
315 TIME_URILOADER_FUNCTION(request
);
317 // if we have retarged to the end stream listener, then forward the call....
318 // otherwise, don't do anything
322 if (m_targetStreamListener
)
323 rv
= m_targetStreamListener
->OnDataAvailable(request
, aCtxt
, inStr
, sourceOffset
, count
);
327 NS_IMETHODIMP
nsDocumentOpenInfo::OnStopRequest(nsIRequest
*request
, nsISupports
*aCtxt
,
330 TIME_URILOADER_FUNCTION(request
);
332 LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
334 if ( m_targetStreamListener
)
336 nsCOMPtr
<nsIStreamListener
> listener(m_targetStreamListener
);
338 // If this is a multipart stream, we could get another
339 // OnStartRequest after this... reset state.
340 m_targetStreamListener
= 0;
341 mContentType
.Truncate();
342 listener
->OnStopRequest(request
, aCtxt
, aStatus
);
346 // In the case of multiplexed streams (such as multipart/x-mixed-replace)
347 // these stream listener methods could be called again :-)
352 nsresult
nsDocumentOpenInfo::DispatchContent(nsIRequest
*request
, nsISupports
* aCtxt
)
354 TIME_URILOADER_FUNCTION(request
);
356 LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this, mContentType
.get()));
358 NS_PRECONDITION(!m_targetStreamListener
,
359 "Why do we already have a target stream listener?");
362 nsCOMPtr
<nsIChannel
> aChannel
= do_QueryInterface(request
);
364 LOG_ERROR((" Request is not a channel. Bailing."));
365 return NS_ERROR_FAILURE
;
368 NS_NAMED_LITERAL_CSTRING(anyType
, "*/*");
369 if (mContentType
.IsEmpty() || mContentType
== anyType
) {
370 rv
= aChannel
->GetContentType(mContentType
);
371 if (NS_FAILED(rv
)) return rv
;
372 LOG((" Got type from channel: '%s'", mContentType
.get()));
375 PRBool isGuessFromExt
=
376 mContentType
.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT
);
377 if (isGuessFromExt
) {
378 // Reset to application/octet-stream for now; no one other than the
379 // external helper app service should see APPLICATION_GUESS_FROM_EXT.
380 mContentType
= APPLICATION_OCTET_STREAM
;
381 aChannel
->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM
));
384 // Check whether the data should be forced to be handled externally. This
385 // could happen because the Content-Disposition header is set so, or, in the
386 // future, because the user has specified external handling for the MIME
388 PRBool forceExternalHandling
= PR_FALSE
;
389 nsCAutoString disposition
;
390 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(request
));
391 nsCOMPtr
<nsIURI
> uri
;
394 rv
= httpChannel
->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"),
396 httpChannel
->GetURI(getter_AddRefs(uri
));
400 nsCOMPtr
<nsIMultiPartChannel
> multipartChannel(do_QueryInterface(request
));
401 if (multipartChannel
)
403 rv
= multipartChannel
->GetContentDisposition(disposition
);
405 // Soon-to-be common way to get Disposition: right now only JARChannel
406 rv
= NS_GetContentDisposition(request
, disposition
);
410 LOG((" Disposition header: '%s'", disposition
.get()));
412 if (NS_SUCCEEDED(rv
) && !disposition
.IsEmpty())
414 nsCOMPtr
<nsIMIMEHeaderParam
> mimehdrpar
= do_GetService(NS_MIMEHEADERPARAM_CONTRACTID
, &rv
);
415 if (NS_SUCCEEDED(rv
))
417 nsCAutoString fallbackCharset
;
419 uri
->GetOriginCharset(fallbackCharset
);
420 nsAutoString dispToken
;
421 // Get the disposition type
422 rv
= mimehdrpar
->GetParameter(disposition
, "", fallbackCharset
,
423 PR_TRUE
, nsnull
, dispToken
);
424 // RFC 2183, section 2.8 says that an unknown disposition
425 // value should be treated as "attachment"
426 // XXXbz this code is duplicated in GetFilenameAndExtensionFromChannel in
427 // nsExternalHelperAppService. Factor it out!
429 (!dispToken
.IsEmpty() &&
430 !dispToken
.LowerCaseEqualsLiteral("inline") &&
431 // Broken sites just send
432 // Content-Disposition: filename="file"
433 // without a disposition token... screen those out.
434 !dispToken
.EqualsIgnoreCase("filename", 8) &&
435 // Also in use is Content-Disposition: name="file"
436 !dispToken
.EqualsIgnoreCase("name", 4)))
437 // We have a content-disposition of "attachment" or unknown
438 forceExternalHandling
= PR_TRUE
;
442 LOG((" forceExternalHandling: %s", forceExternalHandling
? "yes" : "no"));
444 // We're going to try to find a contentListener that can handle our data
445 nsCOMPtr
<nsIURIContentListener
> contentListener
;
446 // The type or data the contentListener wants.
447 nsXPIDLCString desiredContentType
;
449 if (!forceExternalHandling
)
452 // First step: See whether m_contentListener wants to handle this
455 if (m_contentListener
&& TryContentListener(m_contentListener
, aChannel
)) {
456 LOG((" Success! Our default listener likes this type"));
461 // If we aren't allowed to try other listeners, just skip through to
462 // trying to convert the data.
463 if (!(mFlags
& nsIURILoader::DONT_RETARGET
)) {
466 // Second step: See whether some other registered listener wants
467 // to handle this content type.
469 PRInt32 count
= mURILoader
->m_listeners
.Count();
470 nsCOMPtr
<nsIURIContentListener
> listener
;
471 for (PRInt32 i
= 0; i
< count
; i
++) {
472 listener
= do_QueryReferent(mURILoader
->m_listeners
[i
]);
474 if (TryContentListener(listener
, aChannel
)) {
475 LOG((" Found listener registered on the URILoader"));
479 // remove from the listener list, reset i and update count
480 mURILoader
->m_listeners
.RemoveObjectAt(i
--);
486 // Third step: Try to find a content listener that has not yet had
487 // the chance to register, as it is contained in a not-yet-loaded
488 // module, but which has registered a contract ID.
490 nsCOMPtr
<nsICategoryManager
> catman
=
491 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
);
493 nsXPIDLCString contractidString
;
494 rv
= catman
->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY
,
496 getter_Copies(contractidString
));
497 if (NS_SUCCEEDED(rv
) && !contractidString
.IsEmpty()) {
498 LOG((" Listener contractid for '%s' is '%s'",
499 mContentType
.get(), contractidString
.get()));
501 listener
= do_CreateInstance(contractidString
);
502 LOG((" Listener from category manager: 0x%p", listener
.get()));
504 if (listener
&& TryContentListener(listener
, aChannel
)) {
505 LOG((" Listener from category manager likes this type"));
512 // Fourth step: try to find an nsIContentHandler for our type.
514 nsCAutoString
handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX
);
515 handlerContractID
+= mContentType
;
517 nsCOMPtr
<nsIContentHandler
> contentHandler
=
518 do_CreateInstance(handlerContractID
.get());
519 if (contentHandler
) {
520 LOG((" Content handler found"));
521 rv
= contentHandler
->HandleContent(mContentType
.get(),
522 m_originalContext
, request
);
523 // XXXbz returning an error code to represent handling the
524 // content is just bizarre!
525 if (rv
!= NS_ERROR_WONT_HANDLE_CONTENT
) {
527 // The content handler has unexpectedly failed. Cancel the request
528 // just in case the handler didn't...
529 LOG((" Content handler failed. Aborting load"));
534 LOG((" Content handler taking over load"));
542 LOG((" DONT_RETARGET flag set, so skipped over random other content "
543 "listeners and content handlers"));
547 // Fifth step: If no listener prefers this type, see if any stream
548 // converters exist to transform this content type into
551 // Don't do this if the server sent us a MIME type of "*/*" because they saw
552 // it in our Accept header and got confused.
553 // XXXbz have to be careful here; may end up in some sort of bizarre infinite
555 if (mContentType
!= anyType
) {
556 rv
= ConvertData(request
, m_contentListener
, mContentType
, anyType
);
558 m_targetStreamListener
= nsnull
;
559 } else if (m_targetStreamListener
) {
560 // We found a converter for this MIME type. We'll just pump data into it
561 // and let the downstream nsDocumentOpenInfo handle things.
562 LOG((" Converter taking over now"));
568 NS_ASSERTION(!m_targetStreamListener
,
569 "If we found a listener, why are we not using it?");
571 if (mFlags
& nsIURILoader::DONT_RETARGET
) {
572 LOG((" External handling forced or (listener not interested and no "
573 "stream converter exists), and retargeting disallowed -> aborting"));
574 return NS_ERROR_WONT_HANDLE_CONTENT
;
577 // Before dispatching to the external helper app service, check for an HTTP
578 // error page. If we got one, we don't want to handle it with a helper app,
581 PRBool requestSucceeded
;
582 httpChannel
->GetRequestSucceeded(&requestSucceeded
);
583 if (!requestSucceeded
) {
584 // returning error from OnStartRequest will cancel the channel
585 return NS_ERROR_FILE_NOT_FOUND
;
591 // All attempts to dispatch this content have failed. Just pass it off to
592 // the helper app service.
594 nsCOMPtr
<nsIExternalHelperAppService
> helperAppService
=
595 do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID
, &rv
);
596 if (helperAppService
) {
597 LOG((" Passing load off to helper app service"));
599 // Set these flags to indicate that the channel has been targeted and that
600 // we are not using the original consumer.
601 nsLoadFlags loadFlags
= 0;
602 request
->GetLoadFlags(&loadFlags
);
603 request
->SetLoadFlags(loadFlags
| nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
604 | nsIChannel::LOAD_TARGETED
);
606 if (isGuessFromExt
) {
607 mContentType
= APPLICATION_GUESS_FROM_EXT
;
608 aChannel
->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT
));
611 rv
= helperAppService
->DoContent(mContentType
,
615 getter_AddRefs(m_targetStreamListener
));
617 request
->SetLoadFlags(loadFlags
);
618 m_targetStreamListener
= nsnull
;
622 NS_ASSERTION(m_targetStreamListener
|| NS_FAILED(rv
),
623 "There is no way we should be successful at this point without a m_targetStreamListener");
628 nsDocumentOpenInfo::ConvertData(nsIRequest
*request
,
629 nsIURIContentListener
* aListener
,
630 const nsACString
& aSrcContentType
,
631 const nsACString
& aOutContentType
)
633 TIME_URILOADER_FUNCTION(request
);
635 LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
636 PromiseFlatCString(aSrcContentType
).get(),
637 PromiseFlatCString(aOutContentType
).get()));
639 NS_PRECONDITION(aSrcContentType
!= aOutContentType
,
640 "ConvertData called when the two types are the same!");
643 nsCOMPtr
<nsIStreamConverterService
> StreamConvService
=
644 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID
, &rv
);
645 if (NS_FAILED(rv
)) return rv
;
647 LOG((" Got converter service"));
649 // When applying stream decoders, it is necessary to "insert" an
650 // intermediate nsDocumentOpenInfo instance to handle the targeting of
651 // the "final" stream or streams.
653 // For certain content types (ie. multi-part/x-mixed-replace) the input
654 // stream is split up into multiple destination streams. This
655 // intermediate instance is used to target these "decoded" streams...
657 nsCOMPtr
<nsDocumentOpenInfo
> nextLink
=
658 new nsDocumentOpenInfo(m_originalContext
, mFlags
, mURILoader
);
659 if (!nextLink
) return NS_ERROR_OUT_OF_MEMORY
;
661 LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink
.get()));
663 // Make sure nextLink starts with the contentListener that said it wanted the
664 // results of this decode.
665 nextLink
->m_contentListener
= aListener
;
666 // Also make sure it has to look for a stream listener to pump data into.
667 nextLink
->m_targetStreamListener
= nsnull
;
669 // Make sure that nextLink treats the data as aOutContentType when
670 // dispatching; that way even if the stream converters don't change the type
671 // on the channel we will still do the right thing. If aOutContentType is
672 // */*, that's OK -- that will just indicate to nextLink that it should get
673 // the type off the channel.
674 nextLink
->mContentType
= aOutContentType
;
676 // The following call sets m_targetStreamListener to the input end of the
677 // stream converter and sets the output end of the stream converter to
678 // nextLink. As we pump data into m_targetStreamListener the stream
679 // converter will convert it and pass the converted data to nextLink.
680 return StreamConvService
->AsyncConvertData(PromiseFlatCString(aSrcContentType
).get(),
681 PromiseFlatCString(aOutContentType
).get(),
684 getter_AddRefs(m_targetStreamListener
));
688 nsDocumentOpenInfo::TryContentListener(nsIURIContentListener
* aListener
,
689 nsIChannel
* aChannel
)
691 TIME_URILOADER_FUNCTION(aChannel
);
693 LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x",
696 NS_PRECONDITION(aListener
, "Must have a non-null listener");
697 NS_PRECONDITION(aChannel
, "Must have a channel");
699 PRBool listenerWantsContent
= PR_FALSE
;
700 nsXPIDLCString typeToUse
;
702 if (mFlags
& nsIURILoader::IS_CONTENT_PREFERRED
) {
703 aListener
->IsPreferred(mContentType
.get(),
704 getter_Copies(typeToUse
),
705 &listenerWantsContent
);
707 aListener
->CanHandleContent(mContentType
.get(), PR_FALSE
,
708 getter_Copies(typeToUse
),
709 &listenerWantsContent
);
711 if (!listenerWantsContent
) {
712 LOG((" Listener is not interested"));
716 if (!typeToUse
.IsEmpty() && typeToUse
!= mContentType
) {
717 // Need to do a conversion here.
719 nsresult rv
= ConvertData(aChannel
, aListener
, mContentType
, typeToUse
);
722 // No conversion path -- we don't want this listener, if we got one
723 m_targetStreamListener
= nsnull
;
726 LOG((" Found conversion: %s", m_targetStreamListener
? "yes" : "no"));
728 // m_targetStreamListener is now the input end of the converter, and we can
729 // just pump the data in there, if it exists. If it does not, we need to
730 // try other nsIURIContentListeners.
731 return m_targetStreamListener
!= nsnull
;
734 // At this point, aListener wants data of type mContentType. Let 'em have
735 // it. But first, if we are retargeting, set an appropriate flag on the
737 nsLoadFlags loadFlags
= 0;
738 aChannel
->GetLoadFlags(&loadFlags
);
740 // Set this flag to indicate that the channel has been targeted at a final
741 // consumer. This load flag is tested in nsDocLoader::OnProgress.
742 nsLoadFlags newLoadFlags
= nsIChannel::LOAD_TARGETED
;
744 nsCOMPtr
<nsIURIContentListener
> originalListener
=
745 do_GetInterface(m_originalContext
);
746 if (originalListener
!= aListener
) {
747 newLoadFlags
|= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
;
749 aChannel
->SetLoadFlags(loadFlags
| newLoadFlags
);
751 PRBool abort
= PR_FALSE
;
752 PRBool isPreferred
= (mFlags
& nsIURILoader::IS_CONTENT_PREFERRED
) != 0;
753 nsresult rv
= aListener
->DoContent(mContentType
.get(),
756 getter_AddRefs(m_targetStreamListener
),
760 LOG_ERROR((" DoContent failed"));
762 // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
763 aChannel
->SetLoadFlags(loadFlags
);
764 m_targetStreamListener
= nsnull
;
769 // Nothing else to do here -- aListener is handling it all. Make
770 // sure m_targetStreamListener is null so we don't do anything
772 LOG((" Listener has aborted the load"));
773 m_targetStreamListener
= nsnull
;
776 NS_ASSERTION(abort
|| m_targetStreamListener
, "DoContent returned no listener?");
778 // aListener is handling the load from this point on.
783 ///////////////////////////////////////////////////////////////////////////////////////////////
784 // Implementation of nsURILoader
785 ///////////////////////////////////////////////////////////////////////////////////////////////
787 nsURILoader::nsURILoader()
791 mLog
= PR_NewLogModule("URILoader");
796 nsURILoader::~nsURILoader()
800 NS_IMPL_ADDREF(nsURILoader
)
801 NS_IMPL_RELEASE(nsURILoader
)
803 NS_INTERFACE_MAP_BEGIN(nsURILoader
)
804 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIURILoader
)
805 NS_INTERFACE_MAP_ENTRY(nsIURILoader
)
808 NS_IMETHODIMP
nsURILoader::RegisterContentListener(nsIURIContentListener
* aContentListener
)
812 nsWeakPtr weakListener
= do_GetWeakReference(aContentListener
);
813 NS_ASSERTION(weakListener
, "your URIContentListener must support weak refs!\n");
816 m_listeners
.AppendObject(weakListener
);
821 NS_IMETHODIMP
nsURILoader::UnRegisterContentListener(nsIURIContentListener
* aContentListener
)
823 nsWeakPtr weakListener
= do_GetWeakReference(aContentListener
);
825 m_listeners
.RemoveObject(weakListener
);
831 NS_IMETHODIMP
nsURILoader::OpenURI(nsIChannel
*channel
,
832 PRBool aIsContentPreferred
,
833 nsIInterfaceRequestor
*aWindowContext
)
835 NS_ENSURE_ARG_POINTER(channel
);
837 TIME_URILOADER_FUNCTION(channel
);
841 nsCOMPtr
<nsIURI
> uri
;
842 channel
->GetURI(getter_AddRefs(uri
));
844 uri
->GetAsciiSpec(spec
);
845 LOG(("nsURILoader::OpenURI for %s", spec
.get()));
849 nsCOMPtr
<nsIStreamListener
> loader
;
850 nsresult rv
= OpenChannel(channel
,
851 aIsContentPreferred
? IS_CONTENT_PREFERRED
: 0,
854 getter_AddRefs(loader
));
856 if (NS_SUCCEEDED(rv
)) {
857 // this method is not complete!!! Eventually, we should first go
858 // to the content listener and ask them for a protocol handler...
859 // if they don't give us one, we need to go to the registry and get
860 // the preferred protocol handler.
862 // But for now, I'm going to let necko do the work for us....
863 rv
= channel
->AsyncOpen(loader
, nsnull
);
865 // no content from this load - that's OK.
866 if (rv
== NS_ERROR_NO_CONTENT
) {
867 LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
870 } else if (rv
== NS_ERROR_WONT_HANDLE_CONTENT
) {
871 // Not really an error, from this method's point of view
877 nsresult
nsURILoader::OpenChannel(nsIChannel
* channel
,
879 nsIInterfaceRequestor
* aWindowContext
,
880 PRBool aChannelIsOpen
,
881 nsIStreamListener
** aListener
)
883 NS_ASSERTION(channel
, "Trying to open a null channel!");
884 NS_ASSERTION(aWindowContext
, "Window context must not be null");
886 TIME_URILOADER_FUNCTION(channel
);
890 nsCOMPtr
<nsIURI
> uri
;
891 channel
->GetURI(getter_AddRefs(uri
));
893 uri
->GetAsciiSpec(spec
);
894 LOG(("nsURILoader::OpenChannel for %s", spec
.get()));
898 // Let the window context's uriListener know that the open is starting. This
899 // gives that window a chance to abort the load process.
900 nsCOMPtr
<nsIURIContentListener
> winContextListener(do_GetInterface(aWindowContext
));
901 if (winContextListener
) {
902 nsCOMPtr
<nsIURI
> uri
;
903 channel
->GetURI(getter_AddRefs(uri
));
905 PRBool doAbort
= PR_FALSE
;
906 winContextListener
->OnStartURIOpen(uri
, &doAbort
);
909 LOG((" OnStartURIOpen aborted load"));
910 return NS_ERROR_WONT_HANDLE_CONTENT
;
915 // we need to create a DocumentOpenInfo object which will go ahead and open
916 // the url and discover the content type....
917 nsCOMPtr
<nsDocumentOpenInfo
> loader
=
918 new nsDocumentOpenInfo(aWindowContext
, aFlags
, this);
920 if (!loader
) return NS_ERROR_OUT_OF_MEMORY
;
922 // Set the correct loadgroup on the channel
923 nsCOMPtr
<nsILoadGroup
> loadGroup(do_GetInterface(aWindowContext
));
926 // XXXbz This context is violating what we'd like to be the new uriloader
927 // api.... Set up a nsDocLoader to handle the loadgroup for this context.
928 // This really needs to go away!
929 nsCOMPtr
<nsIURIContentListener
> listener(do_GetInterface(aWindowContext
));
931 nsCOMPtr
<nsISupports
> cookie
;
932 listener
->GetLoadCookie(getter_AddRefs(cookie
));
934 nsRefPtr
<nsDocLoader
> newDocLoader
= new nsDocLoader();
936 return NS_ERROR_OUT_OF_MEMORY
;
937 nsresult rv
= newDocLoader
->Init();
940 rv
= nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader
);
943 cookie
= nsDocLoader::GetAsSupports(newDocLoader
);
944 listener
->SetLoadCookie(cookie
);
946 loadGroup
= do_GetInterface(cookie
);
950 // If the channel is pending, then we need to remove it from its current
952 nsCOMPtr
<nsILoadGroup
> oldGroup
;
953 channel
->GetLoadGroup(getter_AddRefs(oldGroup
));
954 if (aChannelIsOpen
&& !SameCOMIdentity(oldGroup
, loadGroup
)) {
955 // It is important to add the channel to the new group before
956 // removing it from the old one, so that the load isn't considered
957 // done as soon as the request is removed.
958 loadGroup
->AddRequest(channel
, nsnull
);
961 oldGroup
->RemoveRequest(channel
, nsnull
, NS_BINDING_RETARGETED
);
965 channel
->SetLoadGroup(loadGroup
);
967 // prepare the loader for receiving data
968 nsresult rv
= loader
->Prepare();
969 if (NS_SUCCEEDED(rv
))
970 NS_ADDREF(*aListener
= loader
);
974 NS_IMETHODIMP
nsURILoader::OpenChannel(nsIChannel
* channel
,
976 nsIInterfaceRequestor
* aWindowContext
,
977 nsIStreamListener
** aListener
)
980 if (NS_FAILED(channel
->IsPending(&pending
))) {
984 return OpenChannel(channel
, aFlags
, aWindowContext
, pending
, aListener
);
987 NS_IMETHODIMP
nsURILoader::Stop(nsISupports
* aLoadCookie
)
990 nsCOMPtr
<nsIDocumentLoader
> docLoader
;
992 NS_ENSURE_ARG_POINTER(aLoadCookie
);
994 docLoader
= do_GetInterface(aLoadCookie
, &rv
);
996 rv
= docLoader
->Stop();