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"
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"
84 PRLogModuleInfo
* nsURILoader::mLog
= nsnull
;
87 #define LOG(args) PR_LOG(nsURILoader::mLog, PR_LOG_DEBUG, args)
88 #define LOG_ERROR(args) PR_LOG(nsURILoader::mLog, PR_LOG_ERROR, args)
89 #define LOG_ENABLED() PR_LOG_TEST(nsURILoader::mLog, PR_LOG_DEBUG)
92 * The nsDocumentOpenInfo contains the state required when a single
93 * document is being opened in order to discover the content type...
94 * Each instance remains alive until its target URL has been loaded
97 class nsDocumentOpenInfo
: public nsIStreamListener
100 // Needed for nsCOMPtr to work right... Don't call this!
101 nsDocumentOpenInfo();
104 // aFlags is a combination of the flags on nsIURILoader
105 nsDocumentOpenInfo(nsIInterfaceRequestor
* aWindowContext
,
107 nsURILoader
* aURILoader
);
112 * Prepares this object for receiving data. The stream
113 * listener methods of this class must not be called before calling this
118 // Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
119 // take the data off our hands.
120 nsresult
DispatchContent(nsIRequest
*request
, nsISupports
* aCtxt
);
122 // Call this if we need to insert a stream converter from aSrcContentType to
123 // aOutContentType into the StreamListener chain. DO NOT call it if the two
124 // types are the same, since no conversion is needed in that case.
125 nsresult
ConvertData(nsIRequest
*request
,
126 nsIURIContentListener
*aListener
,
127 const nsACString
& aSrcContentType
,
128 const nsACString
& aOutContentType
);
131 * Function to attempt to use aListener to handle the load. If
132 * PR_TRUE is returned, nothing else needs to be done; if PR_FALSE
133 * is returned, then a different way of handling the load should be
136 PRBool
TryContentListener(nsIURIContentListener
* aListener
,
137 nsIChannel
* aChannel
);
139 // nsIRequestObserver methods:
140 NS_DECL_NSIREQUESTOBSERVER
142 // nsIStreamListener methods:
143 NS_DECL_NSISTREAMLISTENER
146 ~nsDocumentOpenInfo();
150 * The first content listener to try dispatching data to. Typically
151 * the listener associated with the entity that originated the load.
153 nsCOMPtr
<nsIURIContentListener
> m_contentListener
;
156 * The stream listener to forward nsIStreamListener notifications
157 * to. This is set once the load is dispatched.
159 nsCOMPtr
<nsIStreamListener
> m_targetStreamListener
;
162 * A pointer to the entity that originated the load. We depend on getting
163 * things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
165 nsCOMPtr
<nsIInterfaceRequestor
> m_originalContext
;
168 * IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent
169 * (also determines whether we use CanHandleContent or IsPreferred).
170 * DONT_RETARGET means that we will only try m_originalContext, no other
176 * The type of the data we will be trying to dispatch.
178 nsCString mContentType
;
181 * Reference to the URILoader service so we can access its list of
182 * nsIURIContentListeners.
184 nsRefPtr
<nsURILoader
> mURILoader
;
187 NS_IMPL_THREADSAFE_ADDREF(nsDocumentOpenInfo
)
188 NS_IMPL_THREADSAFE_RELEASE(nsDocumentOpenInfo
)
190 NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo
)
191 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIRequestObserver
)
192 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
193 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
194 NS_INTERFACE_MAP_END_THREADSAFE
196 nsDocumentOpenInfo::nsDocumentOpenInfo()
198 NS_NOTREACHED("This should never be called\n");
201 nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor
* aWindowContext
,
203 nsURILoader
* aURILoader
)
204 : m_originalContext(aWindowContext
),
206 mURILoader(aURILoader
)
210 nsDocumentOpenInfo::~nsDocumentOpenInfo()
214 nsresult
nsDocumentOpenInfo::Prepare()
216 LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
220 // ask our window context if it has a uri content listener...
221 m_contentListener
= do_GetInterface(m_originalContext
, &rv
);
225 NS_IMETHODIMP
nsDocumentOpenInfo::OnStartRequest(nsIRequest
*request
, nsISupports
* aCtxt
)
227 LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
232 // Deal with "special" HTTP responses:
234 // - In the case of a 204 (No Content) or 205 (Reset Content) response, do
235 // not try to find a content handler. Return NS_BINDING_ABORTED to cancel
236 // the request. This has the effect of ensuring that the DocLoader does
237 // not try to interpret this as a real request.
239 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(request
, &rv
));
241 if (NS_SUCCEEDED(rv
)) {
242 PRUint32 responseCode
= 0;
244 rv
= httpChannel
->GetResponseStatus(&responseCode
);
247 LOG_ERROR((" Failed to get HTTP response status"));
249 // behave as in the canceled case
253 LOG((" HTTP response status: %d", responseCode
));
255 if (204 == responseCode
|| 205 == responseCode
) {
256 return NS_BINDING_ABORTED
;
261 // Make sure that the transaction has succeeded, so far...
265 rv
= request
->GetStatus(&status
);
267 NS_ASSERTION(NS_SUCCEEDED(rv
), "Unable to get request status!");
268 if (NS_FAILED(rv
)) return rv
;
270 if (NS_FAILED(status
)) {
271 LOG_ERROR((" Request failed, status: 0x%08X", rv
));
274 // The transaction has already reported an error - so it will be torn
275 // down. Therefore, it is not necessary to return an error code...
280 rv
= DispatchContent(request
, aCtxt
);
282 LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08X", m_targetStreamListener
.get(), rv
));
284 NS_ASSERTION(NS_SUCCEEDED(rv
) || !m_targetStreamListener
,
285 "Must not have an m_targetStreamListener with a failure return!");
287 NS_ENSURE_SUCCESS(rv
, rv
);
289 if (m_targetStreamListener
)
290 rv
= m_targetStreamListener
->OnStartRequest(request
, aCtxt
);
292 LOG((" OnStartRequest returning: 0x%08X", rv
));
297 NS_IMETHODIMP
nsDocumentOpenInfo::OnDataAvailable(nsIRequest
*request
, nsISupports
* aCtxt
,
298 nsIInputStream
* inStr
, PRUint32 sourceOffset
, PRUint32 count
)
300 // if we have retarged to the end stream listener, then forward the call....
301 // otherwise, don't do anything
305 if (m_targetStreamListener
)
306 rv
= m_targetStreamListener
->OnDataAvailable(request
, aCtxt
, inStr
, sourceOffset
, count
);
310 NS_IMETHODIMP
nsDocumentOpenInfo::OnStopRequest(nsIRequest
*request
, nsISupports
*aCtxt
,
313 LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
315 if ( m_targetStreamListener
)
317 nsCOMPtr
<nsIStreamListener
> listener(m_targetStreamListener
);
319 // If this is a multipart stream, we could get another
320 // OnStartRequest after this... reset state.
321 m_targetStreamListener
= 0;
322 mContentType
.Truncate();
323 listener
->OnStopRequest(request
, aCtxt
, aStatus
);
327 // In the case of multiplexed streams (such as multipart/x-mixed-replace)
328 // these stream listener methods could be called again :-)
333 nsresult
nsDocumentOpenInfo::DispatchContent(nsIRequest
*request
, nsISupports
* aCtxt
)
335 LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this, mContentType
.get()));
337 NS_PRECONDITION(!m_targetStreamListener
,
338 "Why do we already have a target stream listener?");
341 nsCOMPtr
<nsIChannel
> aChannel
= do_QueryInterface(request
);
343 LOG_ERROR((" Request is not a channel. Bailing."));
344 return NS_ERROR_FAILURE
;
347 NS_NAMED_LITERAL_CSTRING(anyType
, "*/*");
348 if (mContentType
.IsEmpty() || mContentType
== anyType
) {
349 rv
= aChannel
->GetContentType(mContentType
);
350 if (NS_FAILED(rv
)) return rv
;
351 LOG((" Got type from channel: '%s'", mContentType
.get()));
354 PRBool isGuessFromExt
=
355 mContentType
.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT
);
356 if (isGuessFromExt
) {
357 // Reset to application/octet-stream for now; no one other than the
358 // external helper app service should see APPLICATION_GUESS_FROM_EXT.
359 mContentType
= APPLICATION_OCTET_STREAM
;
360 aChannel
->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM
));
363 // Check whether the data should be forced to be handled externally. This
364 // could happen because the Content-Disposition header is set so, or, in the
365 // future, because the user has specified external handling for the MIME
367 PRBool forceExternalHandling
= PR_FALSE
;
368 nsCAutoString disposition
;
369 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(request
));
370 nsCOMPtr
<nsIURI
> uri
;
373 rv
= httpChannel
->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"),
375 httpChannel
->GetURI(getter_AddRefs(uri
));
379 nsCOMPtr
<nsIMultiPartChannel
> multipartChannel(do_QueryInterface(request
));
380 if (multipartChannel
)
382 rv
= multipartChannel
->GetContentDisposition(disposition
);
386 LOG((" Disposition header: '%s'", disposition
.get()));
388 if (NS_SUCCEEDED(rv
) && !disposition
.IsEmpty())
390 nsCOMPtr
<nsIMIMEHeaderParam
> mimehdrpar
= do_GetService(NS_MIMEHEADERPARAM_CONTRACTID
, &rv
);
391 if (NS_SUCCEEDED(rv
))
393 nsCAutoString fallbackCharset
;
395 uri
->GetOriginCharset(fallbackCharset
);
396 nsAutoString dispToken
;
397 // Get the disposition type
398 rv
= mimehdrpar
->GetParameter(disposition
, "", fallbackCharset
,
399 PR_TRUE
, nsnull
, dispToken
);
400 // RFC 2183, section 2.8 says that an unknown disposition
401 // value should be treated as "attachment"
402 // XXXbz this code is duplicated in GetFilenameAndExtensionFromChannel in
403 // nsExternalHelperAppService. Factor it out!
405 (// Some broken sites just send
406 // Content-Disposition: ; filename="file"
407 // screen those out here.
408 !dispToken
.IsEmpty() &&
409 !dispToken
.LowerCaseEqualsLiteral("inline") &&
410 // Broken sites just send
411 // Content-Disposition: filename="file"
412 // without a disposition token... screen those out.
413 !dispToken
.EqualsIgnoreCase("filename", 8)) &&
414 // Also in use is Content-Disposition: name="file"
415 !dispToken
.EqualsIgnoreCase("name", 4))
416 // We have a content-disposition of "attachment" or unknown
417 forceExternalHandling
= PR_TRUE
;
421 LOG((" forceExternalHandling: %s", forceExternalHandling
? "yes" : "no"));
423 // We're going to try to find a contentListener that can handle our data
424 nsCOMPtr
<nsIURIContentListener
> contentListener
;
425 // The type or data the contentListener wants.
426 nsXPIDLCString desiredContentType
;
428 if (!forceExternalHandling
)
431 // First step: See whether m_contentListener wants to handle this
434 if (m_contentListener
&& TryContentListener(m_contentListener
, aChannel
)) {
435 LOG((" Success! Our default listener likes this type"));
440 // If we aren't allowed to try other listeners, we're done here.
441 if (!(mFlags
& nsIURILoader::DONT_RETARGET
)) {
444 // Second step: See whether some other registered listener wants
445 // to handle this content type.
447 PRInt32 count
= mURILoader
->m_listeners
.Count();
448 nsCOMPtr
<nsIURIContentListener
> listener
;
449 for (PRInt32 i
= 0; i
< count
; i
++) {
450 listener
= do_QueryReferent(mURILoader
->m_listeners
[i
]);
452 if (TryContentListener(listener
, aChannel
)) {
453 LOG((" Found listener registered on the URILoader"));
457 // remove from the listener list, reset i and update count
458 mURILoader
->m_listeners
.RemoveObjectAt(i
--);
464 // Third step: Try to find a content listener that has not yet had
465 // the chance to register, as it is contained in a not-yet-loaded
466 // module, but which has registered a contract ID.
468 nsCOMPtr
<nsICategoryManager
> catman
=
469 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
);
471 nsXPIDLCString contractidString
;
472 rv
= catman
->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY
,
474 getter_Copies(contractidString
));
475 if (NS_SUCCEEDED(rv
) && !contractidString
.IsEmpty()) {
476 LOG((" Listener contractid for '%s' is '%s'",
477 mContentType
.get(), contractidString
.get()));
479 listener
= do_CreateInstance(contractidString
);
480 LOG((" Listener from category manager: 0x%p", listener
.get()));
482 if (listener
&& TryContentListener(listener
, aChannel
)) {
483 LOG((" Listener from category manager likes this type"));
490 // Fourth step: try to find an nsIContentHandler for our type.
492 nsCAutoString
handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX
);
493 handlerContractID
+= mContentType
;
495 nsCOMPtr
<nsIContentHandler
> contentHandler
=
496 do_CreateInstance(handlerContractID
.get());
497 if (contentHandler
) {
498 LOG((" Content handler found"));
499 rv
= contentHandler
->HandleContent(mContentType
.get(),
500 m_originalContext
, request
);
501 // XXXbz returning an error code to represent handling the
502 // content is just bizarre!
503 if (rv
!= NS_ERROR_WONT_HANDLE_CONTENT
) {
505 // The content handler has unexpectedly failed. Cancel the request
506 // just in case the handler didn't...
507 LOG((" Content handler failed. Aborting load"));
512 LOG((" Content handler taking over load"));
520 } else if (mFlags
& nsIURILoader::DONT_RETARGET
) {
521 // External handling was forced, but we must not retarget
523 LOG((" External handling forced, but not allowed to retarget -> aborting"));
524 return NS_ERROR_WONT_HANDLE_CONTENT
;
527 NS_ASSERTION(!m_targetStreamListener
,
528 "If we found a listener, why are we not using it?");
531 // Fifth step: If no listener prefers this type, see if any stream
532 // converters exist to transform this content type into
536 // We always want to do this, since even content being forced to
537 // be handled externally may need decoding (eg via the unknown
539 // Don't do this if the server sent us a MIME type of "*/*" because they saw
540 // it in our Accept header and got confused.
541 // XXXbz have to be careful here; may end up in some sort of bizarre infinite
543 if (mContentType
!= anyType
) {
544 rv
= ConvertData(request
, m_contentListener
, mContentType
, anyType
);
546 m_targetStreamListener
= nsnull
;
547 } else if (m_targetStreamListener
) {
548 // We found a converter for this MIME type. We'll just pump data into it
549 // and let the downstream nsDocumentOpenInfo handle things.
550 LOG((" Converter taking over now"));
555 if (mFlags
& nsIURILoader::DONT_RETARGET
) {
556 LOG((" Listener not interested and no stream converter exists, and retargeting disallowed -> aborting"));
557 return NS_ERROR_WONT_HANDLE_CONTENT
;
560 // Before dispatching to the external helper app service, check for an HTTP
561 // error page. If we got one, we don't want to handle it with a helper app,
564 PRBool requestSucceeded
;
565 httpChannel
->GetRequestSucceeded(&requestSucceeded
);
566 if (!requestSucceeded
) {
567 // returning error from OnStartRequest will cancel the channel
568 return NS_ERROR_FILE_NOT_FOUND
;
574 // All attempts to dispatch this content have failed. Just pass it off to
575 // the helper app service.
577 nsCOMPtr
<nsIExternalHelperAppService
> helperAppService
=
578 do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID
, &rv
);
579 if (helperAppService
) {
580 LOG((" Passing load off to helper app service"));
582 // Set these flags to indicate that the channel has been targeted and that
583 // we are not using the original consumer.
584 nsLoadFlags loadFlags
= 0;
585 request
->GetLoadFlags(&loadFlags
);
586 request
->SetLoadFlags(loadFlags
| nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
587 | nsIChannel::LOAD_TARGETED
);
589 if (isGuessFromExt
) {
590 mContentType
= APPLICATION_GUESS_FROM_EXT
;
591 aChannel
->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT
));
594 rv
= helperAppService
->DoContent(mContentType
,
597 getter_AddRefs(m_targetStreamListener
));
599 request
->SetLoadFlags(loadFlags
);
600 m_targetStreamListener
= nsnull
;
604 NS_ASSERTION(m_targetStreamListener
|| NS_FAILED(rv
),
605 "There is no way we should be successful at this point without a m_targetStreamListener");
610 nsDocumentOpenInfo::ConvertData(nsIRequest
*request
,
611 nsIURIContentListener
* aListener
,
612 const nsACString
& aSrcContentType
,
613 const nsACString
& aOutContentType
)
615 LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
616 PromiseFlatCString(aSrcContentType
).get(),
617 PromiseFlatCString(aOutContentType
).get()));
619 NS_PRECONDITION(aSrcContentType
!= aOutContentType
,
620 "ConvertData called when the two types are the same!");
623 nsCOMPtr
<nsIStreamConverterService
> StreamConvService
=
624 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID
, &rv
);
625 if (NS_FAILED(rv
)) return rv
;
627 LOG((" Got converter service"));
629 // When applying stream decoders, it is necessary to "insert" an
630 // intermediate nsDocumentOpenInfo instance to handle the targeting of
631 // the "final" stream or streams.
633 // For certain content types (ie. multi-part/x-mixed-replace) the input
634 // stream is split up into multiple destination streams. This
635 // intermediate instance is used to target these "decoded" streams...
637 nsCOMPtr
<nsDocumentOpenInfo
> nextLink
=
638 new nsDocumentOpenInfo(m_originalContext
, mFlags
, mURILoader
);
639 if (!nextLink
) return NS_ERROR_OUT_OF_MEMORY
;
641 LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink
.get()));
643 // Make sure nextLink starts with the contentListener that said it wanted the
644 // results of this decode.
645 nextLink
->m_contentListener
= aListener
;
646 // Also make sure it has to look for a stream listener to pump data into.
647 nextLink
->m_targetStreamListener
= nsnull
;
649 // Make sure that nextLink treats the data as aOutContentType when
650 // dispatching; that way even if the stream converters don't change the type
651 // on the channel we will still do the right thing. If aOutContentType is
652 // */*, that's OK -- that will just indicate to nextLink that it should get
653 // the type off the channel.
654 nextLink
->mContentType
= aOutContentType
;
656 // The following call sets m_targetStreamListener to the input end of the
657 // stream converter and sets the output end of the stream converter to
658 // nextLink. As we pump data into m_targetStreamListener the stream
659 // converter will convert it and pass the converted data to nextLink.
660 return StreamConvService
->AsyncConvertData(PromiseFlatCString(aSrcContentType
).get(),
661 PromiseFlatCString(aOutContentType
).get(),
664 getter_AddRefs(m_targetStreamListener
));
668 nsDocumentOpenInfo::TryContentListener(nsIURIContentListener
* aListener
,
669 nsIChannel
* aChannel
)
671 LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x",
674 NS_PRECONDITION(aListener
, "Must have a non-null listener");
675 NS_PRECONDITION(aChannel
, "Must have a channel");
677 PRBool listenerWantsContent
= PR_FALSE
;
678 nsXPIDLCString typeToUse
;
680 if (mFlags
& nsIURILoader::IS_CONTENT_PREFERRED
) {
681 aListener
->IsPreferred(mContentType
.get(),
682 getter_Copies(typeToUse
),
683 &listenerWantsContent
);
685 aListener
->CanHandleContent(mContentType
.get(), PR_FALSE
,
686 getter_Copies(typeToUse
),
687 &listenerWantsContent
);
689 if (!listenerWantsContent
) {
690 LOG((" Listener is not interested"));
694 if (!typeToUse
.IsEmpty() && typeToUse
!= mContentType
) {
695 // Need to do a conversion here.
697 nsresult rv
= ConvertData(aChannel
, aListener
, mContentType
, typeToUse
);
700 // No conversion path -- we don't want this listener, if we got one
701 m_targetStreamListener
= nsnull
;
704 LOG((" Found conversion: %s", m_targetStreamListener
? "yes" : "no"));
706 // m_targetStreamListener is now the input end of the converter, and we can
707 // just pump the data in there, if it exists. If it does not, we need to
708 // try other nsIURIContentListeners.
709 return m_targetStreamListener
!= nsnull
;
712 // At this point, aListener wants data of type mContentType. Let 'em have
713 // it. But first, if we are retargeting, set an appropriate flag on the
715 nsLoadFlags loadFlags
= 0;
716 aChannel
->GetLoadFlags(&loadFlags
);
718 // Set this flag to indicate that the channel has been targeted at a final
719 // consumer. This load flag is tested in nsDocLoader::OnProgress.
720 nsLoadFlags newLoadFlags
= nsIChannel::LOAD_TARGETED
;
722 nsCOMPtr
<nsIURIContentListener
> originalListener
=
723 do_GetInterface(m_originalContext
);
724 if (originalListener
!= aListener
) {
725 newLoadFlags
|= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
;
727 aChannel
->SetLoadFlags(loadFlags
| newLoadFlags
);
729 PRBool abort
= PR_FALSE
;
730 PRBool isPreferred
= (mFlags
& nsIURILoader::IS_CONTENT_PREFERRED
) != 0;
731 nsresult rv
= aListener
->DoContent(mContentType
.get(),
734 getter_AddRefs(m_targetStreamListener
),
738 LOG_ERROR((" DoContent failed"));
740 // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
741 aChannel
->SetLoadFlags(loadFlags
);
742 m_targetStreamListener
= nsnull
;
747 // Nothing else to do here -- aListener is handling it all. Make
748 // sure m_targetStreamListener is null so we don't do anything
750 LOG((" Listener has aborted the load"));
751 m_targetStreamListener
= nsnull
;
754 NS_ASSERTION(abort
|| m_targetStreamListener
, "DoContent returned no listener?");
756 // aListener is handling the load from this point on.
761 ///////////////////////////////////////////////////////////////////////////////////////////////
762 // Implementation of nsURILoader
763 ///////////////////////////////////////////////////////////////////////////////////////////////
765 nsURILoader::nsURILoader()
769 mLog
= PR_NewLogModule("URILoader");
774 nsURILoader::~nsURILoader()
778 NS_IMPL_ADDREF(nsURILoader
)
779 NS_IMPL_RELEASE(nsURILoader
)
781 NS_INTERFACE_MAP_BEGIN(nsURILoader
)
782 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIURILoader
)
783 NS_INTERFACE_MAP_ENTRY(nsIURILoader
)
786 NS_IMETHODIMP
nsURILoader::RegisterContentListener(nsIURIContentListener
* aContentListener
)
790 nsWeakPtr weakListener
= do_GetWeakReference(aContentListener
);
791 NS_ASSERTION(weakListener
, "your URIContentListener must support weak refs!\n");
794 m_listeners
.AppendObject(weakListener
);
799 NS_IMETHODIMP
nsURILoader::UnRegisterContentListener(nsIURIContentListener
* aContentListener
)
801 nsWeakPtr weakListener
= do_GetWeakReference(aContentListener
);
803 m_listeners
.RemoveObject(weakListener
);
809 NS_IMETHODIMP
nsURILoader::OpenURI(nsIChannel
*channel
,
810 PRBool aIsContentPreferred
,
811 nsIInterfaceRequestor
*aWindowContext
)
813 NS_ENSURE_ARG_POINTER(channel
);
817 nsCOMPtr
<nsIURI
> uri
;
818 channel
->GetURI(getter_AddRefs(uri
));
820 uri
->GetAsciiSpec(spec
);
821 LOG(("nsURILoader::OpenURI for %s", spec
.get()));
825 nsCOMPtr
<nsIStreamListener
> loader
;
826 nsresult rv
= OpenChannel(channel
,
827 aIsContentPreferred
? IS_CONTENT_PREFERRED
: 0,
830 getter_AddRefs(loader
));
832 if (NS_SUCCEEDED(rv
)) {
833 // this method is not complete!!! Eventually, we should first go
834 // to the content listener and ask them for a protocol handler...
835 // if they don't give us one, we need to go to the registry and get
836 // the preferred protocol handler.
838 // But for now, I'm going to let necko do the work for us....
839 rv
= channel
->AsyncOpen(loader
, nsnull
);
841 // no content from this load - that's OK.
842 if (rv
== NS_ERROR_NO_CONTENT
) {
843 LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
846 } else if (rv
== NS_ERROR_WONT_HANDLE_CONTENT
) {
847 // Not really an error, from this method's point of view
853 nsresult
nsURILoader::OpenChannel(nsIChannel
* channel
,
855 nsIInterfaceRequestor
* aWindowContext
,
856 PRBool aChannelIsOpen
,
857 nsIStreamListener
** aListener
)
859 NS_ASSERTION(channel
, "Trying to open a null channel!");
860 NS_ASSERTION(aWindowContext
, "Window context must not be null");
864 nsCOMPtr
<nsIURI
> uri
;
865 channel
->GetURI(getter_AddRefs(uri
));
867 uri
->GetAsciiSpec(spec
);
868 LOG(("nsURILoader::OpenChannel for %s", spec
.get()));
872 // Let the window context's uriListener know that the open is starting. This
873 // gives that window a chance to abort the load process.
874 nsCOMPtr
<nsIURIContentListener
> winContextListener(do_GetInterface(aWindowContext
));
875 if (winContextListener
) {
876 nsCOMPtr
<nsIURI
> uri
;
877 channel
->GetURI(getter_AddRefs(uri
));
879 PRBool doAbort
= PR_FALSE
;
880 winContextListener
->OnStartURIOpen(uri
, &doAbort
);
883 LOG((" OnStartURIOpen aborted load"));
884 return NS_ERROR_WONT_HANDLE_CONTENT
;
889 // we need to create a DocumentOpenInfo object which will go ahead and open
890 // the url and discover the content type....
891 nsCOMPtr
<nsDocumentOpenInfo
> loader
=
892 new nsDocumentOpenInfo(aWindowContext
, aFlags
, this);
894 if (!loader
) return NS_ERROR_OUT_OF_MEMORY
;
896 // Set the correct loadgroup on the channel
897 nsCOMPtr
<nsILoadGroup
> loadGroup(do_GetInterface(aWindowContext
));
900 // XXXbz This context is violating what we'd like to be the new uriloader
901 // api.... Set up a nsDocLoader to handle the loadgroup for this context.
902 // This really needs to go away!
903 nsCOMPtr
<nsIURIContentListener
> listener(do_GetInterface(aWindowContext
));
905 nsCOMPtr
<nsISupports
> cookie
;
906 listener
->GetLoadCookie(getter_AddRefs(cookie
));
908 nsRefPtr
<nsDocLoader
> newDocLoader
= new nsDocLoader();
910 return NS_ERROR_OUT_OF_MEMORY
;
911 nsresult rv
= newDocLoader
->Init();
914 rv
= nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader
);
917 cookie
= nsDocLoader::GetAsSupports(newDocLoader
);
918 listener
->SetLoadCookie(cookie
);
920 loadGroup
= do_GetInterface(cookie
);
924 // If the channel is pending, then we need to remove it from its current
926 nsCOMPtr
<nsILoadGroup
> oldGroup
;
927 channel
->GetLoadGroup(getter_AddRefs(oldGroup
));
928 if (aChannelIsOpen
&& !SameCOMIdentity(oldGroup
, loadGroup
)) {
929 // It is important to add the channel to the new group before
930 // removing it from the old one, so that the load isn't considered
931 // done as soon as the request is removed.
932 loadGroup
->AddRequest(channel
, nsnull
);
935 oldGroup
->RemoveRequest(channel
, nsnull
, NS_BINDING_RETARGETED
);
939 channel
->SetLoadGroup(loadGroup
);
941 // prepare the loader for receiving data
942 nsresult rv
= loader
->Prepare();
943 if (NS_SUCCEEDED(rv
))
944 NS_ADDREF(*aListener
= loader
);
948 NS_IMETHODIMP
nsURILoader::OpenChannel(nsIChannel
* channel
,
950 nsIInterfaceRequestor
* aWindowContext
,
951 nsIStreamListener
** aListener
)
954 if (NS_FAILED(channel
->IsPending(&pending
))) {
958 return OpenChannel(channel
, aFlags
, aWindowContext
, pending
, aListener
);
961 NS_IMETHODIMP
nsURILoader::Stop(nsISupports
* aLoadCookie
)
964 nsCOMPtr
<nsIDocumentLoader
> docLoader
;
966 NS_ENSURE_ARG_POINTER(aLoadCookie
);
968 docLoader
= do_GetInterface(aLoadCookie
, &rv
);
970 rv
= docLoader
->Stop();