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 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsURILoader.h"
8 #include "nsComponentManagerUtils.h"
9 #include "nsIURIContentListener.h"
10 #include "nsIContentHandler.h"
11 #include "nsILoadGroup.h"
12 #include "nsIDocumentLoader.h"
13 #include "nsIStreamListener.h"
15 #include "nsIChannel.h"
16 #include "nsIInterfaceRequestor.h"
17 #include "nsIInterfaceRequestorUtils.h"
18 #include "nsIInputStream.h"
19 #include "nsIStreamConverterService.h"
20 #include "nsIWeakReferenceUtils.h"
21 #include "nsIHttpChannel.h"
24 #include "nsIDocShell.h"
25 #include "nsIThreadRetargetableStreamListener.h"
26 #include "nsIChildChannel.h"
27 #include "nsExternalHelperAppService.h"
30 #include "nsThreadUtils.h"
31 #include "nsReadableUtils.h"
34 #include "nsICategoryManager.h"
35 #include "nsCExternalHandlerService.h"
39 #include "nsMimeTypes.h"
41 #include "nsDocLoader.h"
42 #include "mozilla/Attributes.h"
43 #include "mozilla/IntegerPrintfMacros.h"
44 #include "mozilla/Preferences.h"
45 #include "mozilla/Unused.h"
46 #include "mozilla/StaticPrefs_browser.h"
47 #include "mozilla/StaticPrefs_dom.h"
48 #include "mozilla/StaticPrefs_general.h"
49 #include "nsContentUtils.h"
51 mozilla::LazyLogModule
nsURILoader::mLog("URILoader");
53 #define LOG(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Debug, args)
54 #define LOG_ERROR(args) \
55 MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Error, args)
56 #define LOG_ENABLED() MOZ_LOG_TEST(nsURILoader::mLog, mozilla::LogLevel::Debug)
58 NS_IMPL_ADDREF(nsDocumentOpenInfo
)
59 NS_IMPL_RELEASE(nsDocumentOpenInfo
)
61 NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo
)
62 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIRequestObserver
)
63 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
64 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
65 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener
)
68 nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor
* aWindowContext
,
69 uint32_t aFlags
, nsURILoader
* aURILoader
)
70 : m_originalContext(aWindowContext
),
72 mURILoader(aURILoader
),
73 mDataConversionDepthLimit(
74 mozilla::StaticPrefs::
75 general_document_open_conversion_depth_limit()) {}
77 nsDocumentOpenInfo::nsDocumentOpenInfo(uint32_t aFlags
,
78 bool aAllowListenerConversions
)
79 : m_originalContext(nullptr),
82 mDataConversionDepthLimit(
83 mozilla::StaticPrefs::general_document_open_conversion_depth_limit()),
84 mAllowListenerConversions(aAllowListenerConversions
) {}
86 nsDocumentOpenInfo::~nsDocumentOpenInfo() {}
88 nsresult
nsDocumentOpenInfo::Prepare() {
89 LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
93 // ask our window context if it has a uri content listener...
94 m_contentListener
= do_GetInterface(m_originalContext
, &rv
);
98 NS_IMETHODIMP
nsDocumentOpenInfo::OnStartRequest(nsIRequest
* request
) {
99 LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
102 return NS_ERROR_UNEXPECTED
;
108 // Deal with "special" HTTP responses:
110 // - In the case of a 204 (No Content) or 205 (Reset Content) response, do
111 // not try to find a content handler. Return NS_BINDING_ABORTED to cancel
112 // the request. This has the effect of ensuring that the DocLoader does
113 // not try to interpret this as a real request.
115 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(request
, &rv
));
117 if (NS_SUCCEEDED(rv
)) {
118 uint32_t responseCode
= 0;
120 rv
= httpChannel
->GetResponseStatus(&responseCode
);
123 LOG_ERROR((" Failed to get HTTP response status"));
125 // behave as in the canceled case
129 LOG((" HTTP response status: %d", responseCode
));
131 if (204 == responseCode
|| 205 == responseCode
) {
132 return NS_BINDING_ABORTED
;
137 // Make sure that the transaction has succeeded, so far...
141 rv
= request
->GetStatus(&status
);
143 NS_ASSERTION(NS_SUCCEEDED(rv
), "Unable to get request status!");
144 if (NS_FAILED(rv
)) return rv
;
146 if (NS_FAILED(status
)) {
147 LOG_ERROR((" Request failed, status: 0x%08" PRIX32
,
148 static_cast<uint32_t>(status
)));
151 // The transaction has already reported an error - so it will be torn
152 // down. Therefore, it is not necessary to return an error code...
157 rv
= DispatchContent(request
);
159 LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08" PRIX32
,
160 m_targetStreamListener
.get(), static_cast<uint32_t>(rv
)));
163 NS_SUCCEEDED(rv
) || !m_targetStreamListener
,
164 "Must not have an m_targetStreamListener with a failure return!");
166 NS_ENSURE_SUCCESS(rv
, rv
);
168 if (m_targetStreamListener
)
169 rv
= m_targetStreamListener
->OnStartRequest(request
);
171 LOG((" OnStartRequest returning: 0x%08" PRIX32
, static_cast<uint32_t>(rv
)));
177 nsDocumentOpenInfo::CheckListenerChain() {
178 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
180 nsCOMPtr
<nsIThreadRetargetableStreamListener
> retargetableListener
=
181 do_QueryInterface(m_targetStreamListener
, &rv
);
182 if (retargetableListener
) {
183 rv
= retargetableListener
->CheckListenerChain();
186 ("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv "
188 this, (NS_SUCCEEDED(rv
) ? "success" : "failure"),
189 (nsIStreamListener
*)m_targetStreamListener
, static_cast<uint32_t>(rv
)));
194 nsDocumentOpenInfo::OnDataAvailable(nsIRequest
* request
, nsIInputStream
* inStr
,
195 uint64_t sourceOffset
, uint32_t count
) {
196 // if we have retarged to the end stream listener, then forward the call....
197 // otherwise, don't do anything
201 if (m_targetStreamListener
)
202 rv
= m_targetStreamListener
->OnDataAvailable(request
, inStr
, sourceOffset
,
208 nsDocumentOpenInfo::OnDataFinished(nsresult aStatus
) {
209 if (!m_targetStreamListener
) {
210 return NS_ERROR_FAILURE
;
212 nsCOMPtr
<nsIThreadRetargetableStreamListener
> retargetableListener
=
213 do_QueryInterface(m_targetStreamListener
);
214 if (retargetableListener
) {
215 return retargetableListener
->OnDataFinished(aStatus
);
221 NS_IMETHODIMP
nsDocumentOpenInfo::OnStopRequest(nsIRequest
* request
,
223 LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
225 if (m_targetStreamListener
) {
226 nsCOMPtr
<nsIStreamListener
> listener(m_targetStreamListener
);
228 // If this is a multipart stream, we could get another
229 // OnStartRequest after this... reset state.
230 m_targetStreamListener
= nullptr;
231 mContentType
.Truncate();
232 listener
->OnStopRequest(request
, aStatus
);
234 mUsedContentHandler
= false;
237 // In the case of multiplexed streams (such as multipart/x-mixed-replace)
238 // these stream listener methods could be called again :-)
243 nsresult
nsDocumentOpenInfo::DispatchContent(nsIRequest
* request
) {
244 LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this,
245 mContentType
.get()));
247 MOZ_ASSERT(!m_targetStreamListener
,
248 "Why do we already have a target stream listener?");
251 nsCOMPtr
<nsIChannel
> aChannel
= do_QueryInterface(request
);
253 LOG_ERROR((" Request is not a channel. Bailing."));
254 return NS_ERROR_FAILURE
;
257 constexpr auto anyType
= "*/*"_ns
;
258 if (mContentType
.IsEmpty() || mContentType
== anyType
) {
259 rv
= aChannel
->GetContentType(mContentType
);
260 if (NS_FAILED(rv
)) return rv
;
261 LOG((" Got type from channel: '%s'", mContentType
.get()));
264 bool isGuessFromExt
=
265 mContentType
.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT
);
266 if (isGuessFromExt
) {
267 // Reset to application/octet-stream for now; no one other than the
268 // external helper app service should see APPLICATION_GUESS_FROM_EXT.
269 mContentType
= APPLICATION_OCTET_STREAM
;
270 aChannel
->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM
));
273 // Check whether the data should be forced to be handled externally. This
274 // could happen because the Content-Disposition header is set so, or, in the
275 // future, because the user has specified external handling for the MIME
278 // If we're not going to be able to retarget to an external handler, ignore
279 // content-disposition, and unconditionally try to display the content.
280 // This is used for object/embed tags, which expect to display subresources
281 // marked with an attachment disposition.
282 bool forceExternalHandling
= false;
283 if (!(mFlags
& nsIURILoader::DONT_RETARGET
)) {
284 uint32_t disposition
;
285 rv
= aChannel
->GetContentDisposition(&disposition
);
287 if (NS_SUCCEEDED(rv
) && disposition
== nsIChannel::DISPOSITION_ATTACHMENT
) {
288 forceExternalHandling
= true;
292 LOG((" forceExternalHandling: %s", forceExternalHandling
? "yes" : "no"));
294 if (forceExternalHandling
&&
295 mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline()) {
296 // Check if this is a PDF which should be opened internally. We also handle
297 // octet-streams that look like they might be PDFs based on their extension.
298 bool isPDF
= mContentType
.LowerCaseEqualsASCII(APPLICATION_PDF
);
300 (mContentType
.LowerCaseEqualsASCII(APPLICATION_OCTET_STREAM
) ||
301 mContentType
.IsEmpty())) {
303 aChannel
->GetContentDispositionFilename(flname
);
304 isPDF
= StringEndsWith(flname
, u
".pdf"_ns
);
306 nsCOMPtr
<nsIURI
> uri
;
307 aChannel
->GetURI(getter_AddRefs(uri
));
308 nsCOMPtr
<nsIURL
> url(do_QueryInterface(uri
));
311 url
->GetFileExtension(ext
);
312 isPDF
= ext
.EqualsLiteral("pdf");
317 // For a PDF, check if the preference is set that forces attachments to be
318 // opened inline. If so, treat it as a non-attachment by clearing
319 // 'forceExternalHandling' again. This allows it open a PDF directly
320 // instead of downloading it first. It may still end up being handled by
321 // a helper app depending anyway on the later checks.
323 nsCOMPtr
<nsILoadInfo
> loadInfo
;
324 aChannel
->GetLoadInfo(getter_AddRefs(loadInfo
));
326 nsCOMPtr
<nsIMIMEInfo
> mimeInfo
;
328 nsCOMPtr
<nsIMIMEService
> mimeSvc(
329 do_GetService(NS_MIMESERVICE_CONTRACTID
));
330 NS_ENSURE_TRUE(mimeSvc
, NS_ERROR_FAILURE
);
331 mimeSvc
->GetFromTypeAndExtension(nsLiteralCString(APPLICATION_PDF
), ""_ns
,
332 getter_AddRefs(mimeInfo
));
335 int32_t action
= nsIMIMEInfo::saveToDisk
;
336 mimeInfo
->GetPreferredAction(&action
);
338 bool alwaysAsk
= true;
339 mimeInfo
->GetAlwaysAskBeforeHandling(&alwaysAsk
);
340 forceExternalHandling
=
341 alwaysAsk
|| action
!= nsIMIMEInfo::handleInternally
;
346 if (!forceExternalHandling
) {
348 // First step: See whether m_contentListener wants to handle this
351 if (TryDefaultContentListener(aChannel
)) {
352 LOG((" Success! Our default listener likes this type"));
357 // If we aren't allowed to try other listeners, just skip through to
358 // trying to convert the data.
359 if (!(mFlags
& nsIURILoader::DONT_RETARGET
)) {
361 // Second step: See whether some other registered listener wants
362 // to handle this content type.
364 int32_t count
= mURILoader
? mURILoader
->m_listeners
.Count() : 0;
365 nsCOMPtr
<nsIURIContentListener
> listener
;
366 for (int32_t i
= 0; i
< count
; i
++) {
367 listener
= do_QueryReferent(mURILoader
->m_listeners
[i
]);
369 if (TryContentListener(listener
, aChannel
)) {
370 LOG((" Found listener registered on the URILoader"));
374 // remove from the listener list, reset i and update count
375 mURILoader
->m_listeners
.RemoveObjectAt(i
--);
381 // Third step: Try to find an nsIContentHandler for our type.
383 nsAutoCString
handlerContractID(NS_CONTENT_HANDLER_CONTRACTID_PREFIX
);
384 handlerContractID
+= mContentType
;
386 nsCOMPtr
<nsIContentHandler
> contentHandler
=
387 do_CreateInstance(handlerContractID
.get());
388 if (contentHandler
) {
389 LOG((" Content handler found"));
390 // Note that m_originalContext can be nullptr when running this in
391 // the parent process on behalf on a docshell in the content process,
392 // and in that case we only support content handlers that don't need
394 rv
= contentHandler
->HandleContent(mContentType
.get(),
395 m_originalContext
, request
);
396 // XXXbz returning an error code to represent handling the
397 // content is just bizarre!
398 if (rv
!= NS_ERROR_WONT_HANDLE_CONTENT
) {
400 // The content handler has unexpectedly failed. Cancel the request
401 // just in case the handler didn't...
402 LOG((" Content handler failed. Aborting load"));
405 LOG((" Content handler taking over load"));
406 mUsedContentHandler
= true;
414 (" DONT_RETARGET flag set, so skipped over random other content "
415 "listeners and content handlers"));
419 // Fourth step: If no listener prefers this type, see if any stream
420 // converters exist to transform this content type into
423 // Don't do this if the server sent us a MIME type of "*/*" because they saw
424 // it in our Accept header and got confused.
425 // XXXbz have to be careful here; may end up in some sort of bizarre
426 // infinite decoding loop.
427 if (mContentType
!= anyType
) {
428 rv
= TryStreamConversion(aChannel
);
429 if (NS_SUCCEEDED(rv
)) {
435 NS_ASSERTION(!m_targetStreamListener
,
436 "If we found a listener, why are we not using it?");
438 // Before dispatching to the external helper app service, check for an HTTP
439 // error page. If we got one, we don't want to handle it with a helper app,
441 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(request
));
443 bool requestSucceeded
;
444 rv
= httpChannel
->GetRequestSucceeded(&requestSucceeded
);
445 if (NS_FAILED(rv
) || !requestSucceeded
) {
447 (" Returning NS_ERROR_FILE_NOT_FOUND from "
448 "nsDocumentOpenInfo::DispatchContent due to failed HTTP response"));
449 return NS_ERROR_FILE_NOT_FOUND
;
453 if (mFlags
& nsIURILoader::DONT_RETARGET
) {
455 (" External handling forced or (listener not interested and no "
456 "stream converter exists), and retargeting disallowed -> aborting"));
457 return NS_ERROR_WONT_HANDLE_CONTENT
;
462 // All attempts to dispatch this content have failed. Just pass it off to
463 // the helper app service.
466 nsCOMPtr
<nsIExternalHelperAppService
> helperAppService
=
467 do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID
, &rv
);
468 if (helperAppService
) {
469 LOG((" Passing load off to helper app service"));
471 // Set these flags to indicate that the channel has been targeted and that
472 // we are not using the original consumer.
473 nsLoadFlags loadFlags
= 0;
474 request
->GetLoadFlags(&loadFlags
);
475 request
->SetLoadFlags(loadFlags
| nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
|
476 nsIChannel::LOAD_TARGETED
);
478 if (isGuessFromExt
|| mContentType
.IsEmpty()) {
479 mContentType
= APPLICATION_GUESS_FROM_EXT
;
480 aChannel
->SetContentType(nsLiteralCString(APPLICATION_GUESS_FROM_EXT
));
483 rv
= TryExternalHelperApp(helperAppService
, aChannel
);
485 request
->SetLoadFlags(loadFlags
);
486 m_targetStreamListener
= nullptr;
490 NS_ASSERTION(m_targetStreamListener
|| NS_FAILED(rv
),
491 "There is no way we should be successful at this point without "
492 "a m_targetStreamListener");
496 nsresult
nsDocumentOpenInfo::TryExternalHelperApp(
497 nsIExternalHelperAppService
* aHelperAppService
, nsIChannel
* aChannel
) {
498 return aHelperAppService
->DoContent(mContentType
, aChannel
, m_originalContext
,
500 getter_AddRefs(m_targetStreamListener
));
503 nsresult
nsDocumentOpenInfo::ConvertData(nsIRequest
* request
,
504 nsIURIContentListener
* aListener
,
505 const nsACString
& aSrcContentType
,
506 const nsACString
& aOutContentType
) {
507 LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
508 PromiseFlatCString(aSrcContentType
).get(),
509 PromiseFlatCString(aOutContentType
).get()));
511 if (mDataConversionDepthLimit
== 0) {
513 ("[0x%p] nsDocumentOpenInfo::ConvertData - reached the recursion "
516 // This will fall back to external helper app handling.
517 return NS_ERROR_ABORT
;
520 MOZ_ASSERT(aSrcContentType
!= aOutContentType
,
521 "ConvertData called when the two types are the same!");
525 nsCOMPtr
<nsIStreamConverterService
> StreamConvService
=
526 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID
, &rv
);
527 if (NS_FAILED(rv
)) return rv
;
529 LOG((" Got converter service"));
531 // When applying stream decoders, it is necessary to "insert" an
532 // intermediate nsDocumentOpenInfo instance to handle the targeting of
533 // the "final" stream or streams.
535 // For certain content types (ie. multi-part/x-mixed-replace) the input
536 // stream is split up into multiple destination streams. This
537 // intermediate instance is used to target these "decoded" streams...
539 RefPtr
<nsDocumentOpenInfo
> nextLink
= Clone();
541 LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink
.get()));
543 // Decrease the conversion recursion limit by one to prevent infinite loops.
544 nextLink
->mDataConversionDepthLimit
= mDataConversionDepthLimit
- 1;
546 // Make sure nextLink starts with the contentListener that said it wanted
547 // the results of this decode.
548 nextLink
->m_contentListener
= aListener
;
549 // Also make sure it has to look for a stream listener to pump data into.
550 nextLink
->m_targetStreamListener
= nullptr;
552 // Make sure that nextLink treats the data as aOutContentType when
553 // dispatching; that way even if the stream converters don't change the type
554 // on the channel we will still do the right thing. If aOutContentType is
555 // */*, that's OK -- that will just indicate to nextLink that it should get
556 // the type off the channel.
557 nextLink
->mContentType
= aOutContentType
;
559 // The following call sets m_targetStreamListener to the input end of the
560 // stream converter and sets the output end of the stream converter to
561 // nextLink. As we pump data into m_targetStreamListener the stream
562 // converter will convert it and pass the converted data to nextLink.
563 return StreamConvService
->AsyncConvertData(
564 PromiseFlatCString(aSrcContentType
).get(),
565 PromiseFlatCString(aOutContentType
).get(), nextLink
, request
,
566 getter_AddRefs(m_targetStreamListener
));
569 nsresult
nsDocumentOpenInfo::TryStreamConversion(nsIChannel
* aChannel
) {
570 constexpr auto anyType
= "*/*"_ns
;
572 // A empty content type should be treated like the unknown content type.
573 nsCString
srcContentType(mContentType
);
574 if (srcContentType
.IsEmpty()) {
575 srcContentType
.AssignLiteral(UNKNOWN_CONTENT_TYPE
);
579 ConvertData(aChannel
, m_contentListener
, srcContentType
, anyType
);
581 m_targetStreamListener
= nullptr;
582 } else if (m_targetStreamListener
) {
583 // We found a converter for this MIME type. We'll just pump data into
584 // it and let the downstream nsDocumentOpenInfo handle things.
585 LOG((" Converter taking over now"));
590 bool nsDocumentOpenInfo::TryContentListener(nsIURIContentListener
* aListener
,
591 nsIChannel
* aChannel
) {
592 LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x", this,
595 MOZ_ASSERT(aListener
, "Must have a non-null listener");
596 MOZ_ASSERT(aChannel
, "Must have a channel");
598 bool listenerWantsContent
= false;
601 if (mFlags
& nsIURILoader::IS_CONTENT_PREFERRED
) {
602 aListener
->IsPreferred(mContentType
.get(), getter_Copies(typeToUse
),
603 &listenerWantsContent
);
605 aListener
->CanHandleContent(mContentType
.get(), false,
606 getter_Copies(typeToUse
),
607 &listenerWantsContent
);
609 if (!listenerWantsContent
) {
610 LOG((" Listener is not interested"));
614 if (!typeToUse
.IsEmpty() && typeToUse
!= mContentType
) {
615 // Need to do a conversion here.
617 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
618 if (mAllowListenerConversions
) {
619 rv
= ConvertData(aChannel
, aListener
, mContentType
, typeToUse
);
623 // No conversion path -- we don't want this listener, if we got one
624 m_targetStreamListener
= nullptr;
627 LOG((" Found conversion: %s", m_targetStreamListener
? "yes" : "no"));
629 // m_targetStreamListener is now the input end of the converter, and we can
630 // just pump the data in there, if it exists. If it does not, we need to
631 // try other nsIURIContentListeners.
632 return m_targetStreamListener
!= nullptr;
635 // At this point, aListener wants data of type mContentType. Let 'em have
636 // it. But first, if we are retargeting, set an appropriate flag on the
638 nsLoadFlags loadFlags
= 0;
639 aChannel
->GetLoadFlags(&loadFlags
);
641 // Set this flag to indicate that the channel has been targeted at a final
642 // consumer. This load flag is tested in nsDocLoader::OnProgress.
643 nsLoadFlags newLoadFlags
= nsIChannel::LOAD_TARGETED
;
645 nsCOMPtr
<nsIURIContentListener
> originalListener
=
646 do_GetInterface(m_originalContext
);
647 if (originalListener
!= aListener
) {
648 newLoadFlags
|= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
;
650 aChannel
->SetLoadFlags(loadFlags
| newLoadFlags
);
653 bool isPreferred
= (mFlags
& nsIURILoader::IS_CONTENT_PREFERRED
) != 0;
655 aListener
->DoContent(mContentType
, isPreferred
, aChannel
,
656 getter_AddRefs(m_targetStreamListener
), &abort
);
659 LOG_ERROR((" DoContent failed"));
661 // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
662 aChannel
->SetLoadFlags(loadFlags
);
663 m_targetStreamListener
= nullptr;
668 // Nothing else to do here -- aListener is handling it all. Make
669 // sure m_targetStreamListener is null so we don't do anything
671 LOG((" Listener has aborted the load"));
672 m_targetStreamListener
= nullptr;
675 NS_ASSERTION(abort
|| m_targetStreamListener
,
676 "DoContent returned no listener?");
678 // aListener is handling the load from this point on.
682 bool nsDocumentOpenInfo::TryDefaultContentListener(nsIChannel
* aChannel
) {
683 if (m_contentListener
) {
684 return TryContentListener(m_contentListener
, aChannel
);
689 ///////////////////////////////////////////////////////////////////////////////////////////////
690 // Implementation of nsURILoader
691 ///////////////////////////////////////////////////////////////////////////////////////////////
693 nsURILoader::nsURILoader() {}
695 nsURILoader::~nsURILoader() {}
697 NS_IMPL_ADDREF(nsURILoader
)
698 NS_IMPL_RELEASE(nsURILoader
)
700 NS_INTERFACE_MAP_BEGIN(nsURILoader
)
701 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIURILoader
)
702 NS_INTERFACE_MAP_ENTRY(nsIURILoader
)
705 NS_IMETHODIMP
nsURILoader::RegisterContentListener(
706 nsIURIContentListener
* aContentListener
) {
709 nsWeakPtr weakListener
= do_GetWeakReference(aContentListener
);
710 NS_ASSERTION(weakListener
,
711 "your URIContentListener must support weak refs!\n");
713 if (weakListener
) m_listeners
.AppendObject(weakListener
);
718 NS_IMETHODIMP
nsURILoader::UnRegisterContentListener(
719 nsIURIContentListener
* aContentListener
) {
720 nsWeakPtr weakListener
= do_GetWeakReference(aContentListener
);
721 if (weakListener
) m_listeners
.RemoveObject(weakListener
);
726 NS_IMETHODIMP
nsURILoader::OpenURI(nsIChannel
* channel
, uint32_t aFlags
,
727 nsIInterfaceRequestor
* aWindowContext
) {
728 NS_ENSURE_ARG_POINTER(channel
);
731 nsCOMPtr
<nsIURI
> uri
;
732 channel
->GetURI(getter_AddRefs(uri
));
734 uri
->GetAsciiSpec(spec
);
735 LOG(("nsURILoader::OpenURI for %s", spec
.get()));
738 nsCOMPtr
<nsIStreamListener
> loader
;
739 nsresult rv
= OpenChannel(channel
, aFlags
, aWindowContext
, false,
740 getter_AddRefs(loader
));
742 if (rv
== NS_ERROR_WONT_HANDLE_CONTENT
) {
743 // Not really an error, from this method's point of view
748 // This method is not complete. Eventually, we should first go
749 // to the content listener and ask them for a protocol handler...
750 // if they don't give us one, we need to go to the registry and get
751 // the preferred protocol handler.
753 // But for now, I'm going to let necko do the work for us....
754 rv
= channel
->AsyncOpen(loader
);
756 // no content from this load - that's OK.
757 if (rv
== NS_ERROR_NO_CONTENT
) {
758 LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
764 nsresult
nsURILoader::OpenChannel(nsIChannel
* channel
, uint32_t aFlags
,
765 nsIInterfaceRequestor
* aWindowContext
,
767 nsIStreamListener
** aListener
) {
768 NS_ASSERTION(channel
, "Trying to open a null channel!");
769 NS_ASSERTION(aWindowContext
, "Window context must not be null");
772 nsCOMPtr
<nsIURI
> uri
;
773 channel
->GetURI(getter_AddRefs(uri
));
775 uri
->GetAsciiSpec(spec
);
776 LOG(("nsURILoader::OpenChannel for %s", spec
.get()));
779 // we need to create a DocumentOpenInfo object which will go ahead and open
780 // the url and discover the content type....
781 RefPtr
<nsDocumentOpenInfo
> loader
=
782 new nsDocumentOpenInfo(aWindowContext
, aFlags
, this);
784 // Set the correct loadgroup on the channel
785 nsCOMPtr
<nsILoadGroup
> loadGroup(do_GetInterface(aWindowContext
));
788 // XXXbz This context is violating what we'd like to be the new uriloader
789 // api.... Set up a nsDocLoader to handle the loadgroup for this context.
790 // This really needs to go away!
791 nsCOMPtr
<nsIURIContentListener
> listener(do_GetInterface(aWindowContext
));
793 nsCOMPtr
<nsISupports
> cookie
;
794 listener
->GetLoadCookie(getter_AddRefs(cookie
));
796 RefPtr
<nsDocLoader
> newDocLoader
= new nsDocLoader();
797 nsresult rv
= newDocLoader
->Init();
798 if (NS_FAILED(rv
)) return rv
;
799 rv
= nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader
);
800 if (NS_FAILED(rv
)) return rv
;
801 cookie
= nsDocLoader::GetAsSupports(newDocLoader
);
802 listener
->SetLoadCookie(cookie
);
804 loadGroup
= do_GetInterface(cookie
);
808 // If the channel is pending, then we need to remove it from its current
810 nsCOMPtr
<nsILoadGroup
> oldGroup
;
811 channel
->GetLoadGroup(getter_AddRefs(oldGroup
));
812 if (aChannelIsOpen
&& !SameCOMIdentity(oldGroup
, loadGroup
)) {
813 // It is important to add the channel to the new group before
814 // removing it from the old one, so that the load isn't considered
815 // done as soon as the request is removed.
816 loadGroup
->AddRequest(channel
, nullptr);
819 oldGroup
->RemoveRequest(channel
, nullptr, NS_BINDING_RETARGETED
);
823 channel
->SetLoadGroup(loadGroup
);
825 // prepare the loader for receiving data
826 nsresult rv
= loader
->Prepare();
827 if (NS_SUCCEEDED(rv
)) NS_ADDREF(*aListener
= loader
);
831 NS_IMETHODIMP
nsURILoader::OpenChannel(nsIChannel
* channel
, uint32_t aFlags
,
832 nsIInterfaceRequestor
* aWindowContext
,
833 nsIStreamListener
** aListener
) {
835 if (NS_FAILED(channel
->IsPending(&pending
))) {
839 return OpenChannel(channel
, aFlags
, aWindowContext
, pending
, aListener
);
842 NS_IMETHODIMP
nsURILoader::Stop(nsISupports
* aLoadCookie
) {
844 nsCOMPtr
<nsIDocumentLoader
> docLoader
;
846 NS_ENSURE_ARG_POINTER(aLoadCookie
);
848 docLoader
= do_GetInterface(aLoadCookie
, &rv
);
850 rv
= docLoader
->Stop();