Bug 1772603 [wpt PR 34303] - Add WPT test writing and reading back a 0.5 alpha fillSt...
[gecko.git] / uriloader / base / nsURILoader.cpp
blob4d9536e902e63c754934e3eac3117b6a5d644895
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 "nsIURIContentListener.h"
9 #include "nsIContentHandler.h"
10 #include "nsILoadGroup.h"
11 #include "nsIDocumentLoader.h"
12 #include "nsIStreamListener.h"
13 #include "nsIURL.h"
14 #include "nsIChannel.h"
15 #include "nsIInterfaceRequestor.h"
16 #include "nsIInterfaceRequestorUtils.h"
17 #include "nsIInputStream.h"
18 #include "nsIStreamConverterService.h"
19 #include "nsIWeakReferenceUtils.h"
20 #include "nsIHttpChannel.h"
21 #include "netCore.h"
22 #include "nsCRT.h"
23 #include "nsIDocShell.h"
24 #include "nsIThreadRetargetableStreamListener.h"
25 #include "nsIChildChannel.h"
26 #include "nsExternalHelperAppService.h"
28 #include "nsString.h"
29 #include "nsThreadUtils.h"
30 #include "nsReadableUtils.h"
31 #include "nsError.h"
33 #include "nsICategoryManager.h"
34 #include "nsCExternalHandlerService.h"
36 #include "nsNetCID.h"
38 #include "nsMimeTypes.h"
40 #include "nsDocLoader.h"
41 #include "mozilla/Attributes.h"
42 #include "mozilla/IntegerPrintfMacros.h"
43 #include "mozilla/Preferences.h"
44 #include "mozilla/Unused.h"
45 #include "mozilla/StaticPrefs_browser.h"
46 #include "mozilla/StaticPrefs_dom.h"
47 #include "mozilla/StaticPrefs_general.h"
48 #include "nsContentUtils.h"
50 mozilla::LazyLogModule nsURILoader::mLog("URILoader");
52 #define LOG(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Debug, args)
53 #define LOG_ERROR(args) \
54 MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Error, args)
55 #define LOG_ENABLED() MOZ_LOG_TEST(nsURILoader::mLog, mozilla::LogLevel::Debug)
57 NS_IMPL_ADDREF(nsDocumentOpenInfo)
58 NS_IMPL_RELEASE(nsDocumentOpenInfo)
60 NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
61 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
62 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
63 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
64 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
65 NS_INTERFACE_MAP_END
67 nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
68 uint32_t aFlags, nsURILoader* aURILoader)
69 : m_originalContext(aWindowContext),
70 mFlags(aFlags),
71 mURILoader(aURILoader),
72 mDataConversionDepthLimit(
73 StaticPrefs::general_document_open_conversion_depth_limit()) {}
75 nsDocumentOpenInfo::nsDocumentOpenInfo(uint32_t aFlags,
76 bool aAllowListenerConversions)
77 : m_originalContext(nullptr),
78 mFlags(aFlags),
79 mURILoader(nullptr),
80 mDataConversionDepthLimit(
81 StaticPrefs::general_document_open_conversion_depth_limit()),
82 mAllowListenerConversions(aAllowListenerConversions) {}
84 nsDocumentOpenInfo::~nsDocumentOpenInfo() {}
86 nsresult nsDocumentOpenInfo::Prepare() {
87 LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
89 nsresult rv;
91 // ask our window context if it has a uri content listener...
92 m_contentListener = do_GetInterface(m_originalContext, &rv);
93 return rv;
96 NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest* request) {
97 LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
98 MOZ_ASSERT(request);
99 if (!request) {
100 return NS_ERROR_UNEXPECTED;
103 nsresult rv = NS_OK;
106 // Deal with "special" HTTP responses:
108 // - In the case of a 204 (No Content) or 205 (Reset Content) response, do
109 // not try to find a content handler. Return NS_BINDING_ABORTED to cancel
110 // the request. This has the effect of ensuring that the DocLoader does
111 // not try to interpret this as a real request.
113 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request, &rv));
115 if (NS_SUCCEEDED(rv)) {
116 uint32_t responseCode = 0;
118 rv = httpChannel->GetResponseStatus(&responseCode);
120 if (NS_FAILED(rv)) {
121 LOG_ERROR((" Failed to get HTTP response status"));
123 // behave as in the canceled case
124 return NS_OK;
127 LOG((" HTTP response status: %d", responseCode));
129 if (204 == responseCode || 205 == responseCode) {
130 return NS_BINDING_ABORTED;
135 // Make sure that the transaction has succeeded, so far...
137 nsresult status;
139 rv = request->GetStatus(&status);
141 NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get request status!");
142 if (NS_FAILED(rv)) return rv;
144 if (NS_FAILED(status)) {
145 LOG_ERROR((" Request failed, status: 0x%08" PRIX32,
146 static_cast<uint32_t>(status)));
149 // The transaction has already reported an error - so it will be torn
150 // down. Therefore, it is not necessary to return an error code...
152 return NS_OK;
155 rv = DispatchContent(request);
157 LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08" PRIX32,
158 m_targetStreamListener.get(), static_cast<uint32_t>(rv)));
160 NS_ASSERTION(
161 NS_SUCCEEDED(rv) || !m_targetStreamListener,
162 "Must not have an m_targetStreamListener with a failure return!");
164 NS_ENSURE_SUCCESS(rv, rv);
166 if (m_targetStreamListener)
167 rv = m_targetStreamListener->OnStartRequest(request);
169 LOG((" OnStartRequest returning: 0x%08" PRIX32, static_cast<uint32_t>(rv)));
171 return rv;
174 NS_IMETHODIMP
175 nsDocumentOpenInfo::CheckListenerChain() {
176 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
177 nsresult rv = NS_OK;
178 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
179 do_QueryInterface(m_targetStreamListener, &rv);
180 if (retargetableListener) {
181 rv = retargetableListener->CheckListenerChain();
183 LOG(
184 ("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv "
185 "%" PRIx32,
186 this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
187 (nsIStreamListener*)m_targetStreamListener, static_cast<uint32_t>(rv)));
188 return rv;
191 NS_IMETHODIMP
192 nsDocumentOpenInfo::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr,
193 uint64_t sourceOffset, uint32_t count) {
194 // if we have retarged to the end stream listener, then forward the call....
195 // otherwise, don't do anything
197 nsresult rv = NS_OK;
199 if (m_targetStreamListener)
200 rv = m_targetStreamListener->OnDataAvailable(request, inStr, sourceOffset,
201 count);
202 return rv;
205 NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request,
206 nsresult aStatus) {
207 LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
209 if (m_targetStreamListener) {
210 nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
212 // If this is a multipart stream, we could get another
213 // OnStartRequest after this... reset state.
214 m_targetStreamListener = nullptr;
215 mContentType.Truncate();
216 listener->OnStopRequest(request, aStatus);
218 mUsedContentHandler = false;
220 // Remember...
221 // In the case of multiplexed streams (such as multipart/x-mixed-replace)
222 // these stream listener methods could be called again :-)
224 return NS_OK;
227 nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) {
228 LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this,
229 mContentType.get()));
231 MOZ_ASSERT(!m_targetStreamListener,
232 "Why do we already have a target stream listener?");
234 nsresult rv;
235 nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
236 if (!aChannel) {
237 LOG_ERROR((" Request is not a channel. Bailing."));
238 return NS_ERROR_FAILURE;
241 constexpr auto anyType = "*/*"_ns;
242 if (mContentType.IsEmpty() || mContentType == anyType) {
243 rv = aChannel->GetContentType(mContentType);
244 if (NS_FAILED(rv)) return rv;
245 LOG((" Got type from channel: '%s'", mContentType.get()));
248 bool isGuessFromExt =
249 mContentType.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT);
250 if (isGuessFromExt) {
251 // Reset to application/octet-stream for now; no one other than the
252 // external helper app service should see APPLICATION_GUESS_FROM_EXT.
253 mContentType = APPLICATION_OCTET_STREAM;
254 aChannel->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM));
257 // Check whether the data should be forced to be handled externally. This
258 // could happen because the Content-Disposition header is set so, or, in the
259 // future, because the user has specified external handling for the MIME
260 // type.
261 bool forceExternalHandling = false;
262 uint32_t disposition;
263 rv = aChannel->GetContentDisposition(&disposition);
265 if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT) {
266 forceExternalHandling = true;
269 LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
271 if (forceExternalHandling) {
272 // Check if this is a PDF which should be opened internally. We also handle
273 // octet-streams that look like they might be PDFs based on their extension.
274 bool isPDF = mContentType.LowerCaseEqualsASCII(APPLICATION_PDF);
275 if (!isPDF && mContentType.LowerCaseEqualsASCII(APPLICATION_OCTET_STREAM)) {
276 nsAutoString flname;
277 aChannel->GetContentDispositionFilename(flname);
278 isPDF = StringEndsWith(flname, u".pdf"_ns);
279 if (!isPDF) {
280 nsCOMPtr<nsIURI> uri;
281 aChannel->GetURI(getter_AddRefs(uri));
282 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
283 if (url) {
284 nsAutoCString ext;
285 url->GetFileExtension(ext);
286 isPDF = ext.EqualsLiteral("pdf");
291 // For a PDF, check if it will be handled internally. If so, treat it as a
292 // non-attachment by clearing 'forceExternalHandling' again. This allows it
293 // open a PDF directly instead of downloading it first. It may still
294 // end up being handled by a helper app depending anyway on the later
295 // checks.
296 if (isPDF &&
297 StaticPrefs::browser_download_improvements_to_download_panel()) {
298 nsCOMPtr<nsILoadInfo> loadInfo;
299 aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
301 nsCOMPtr<nsIMIMEInfo> mimeInfo;
303 nsCOMPtr<nsIMIMEService> mimeSvc(
304 do_GetService(NS_MIMESERVICE_CONTRACTID));
305 NS_ENSURE_TRUE(mimeSvc, NS_ERROR_FAILURE);
306 mimeSvc->GetFromTypeAndExtension(nsLiteralCString(APPLICATION_PDF), ""_ns,
307 getter_AddRefs(mimeInfo));
309 if (mimeInfo) {
310 int32_t action = nsIMIMEInfo::saveToDisk;
311 mimeInfo->GetPreferredAction(&action);
313 bool alwaysAsk = true;
314 mimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
315 forceExternalHandling =
316 alwaysAsk || action != nsIMIMEInfo::handleInternally;
321 if (!forceExternalHandling) {
323 // First step: See whether m_contentListener wants to handle this
324 // content type.
326 if (TryDefaultContentListener(aChannel)) {
327 LOG((" Success! Our default listener likes this type"));
328 // All done here
329 return NS_OK;
332 // If we aren't allowed to try other listeners, just skip through to
333 // trying to convert the data.
334 if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
336 // Second step: See whether some other registered listener wants
337 // to handle this content type.
339 int32_t count = mURILoader ? mURILoader->m_listeners.Count() : 0;
340 nsCOMPtr<nsIURIContentListener> listener;
341 for (int32_t i = 0; i < count; i++) {
342 listener = do_QueryReferent(mURILoader->m_listeners[i]);
343 if (listener) {
344 if (TryContentListener(listener, aChannel)) {
345 LOG((" Found listener registered on the URILoader"));
346 return NS_OK;
348 } else {
349 // remove from the listener list, reset i and update count
350 mURILoader->m_listeners.RemoveObjectAt(i--);
351 --count;
356 // Third step: Try to find a content listener that has not yet had
357 // the chance to register, as it is contained in a not-yet-loaded
358 // module, but which has registered a contract ID.
360 nsCOMPtr<nsICategoryManager> catman =
361 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
362 if (catman) {
363 nsCString contractidString;
364 rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
365 mContentType, contractidString);
366 if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
367 LOG((" Listener contractid for '%s' is '%s'", mContentType.get(),
368 contractidString.get()));
370 listener = do_CreateInstance(contractidString.get());
371 LOG((" Listener from category manager: 0x%p", listener.get()));
373 if (listener && TryContentListener(listener, aChannel)) {
374 LOG((" Listener from category manager likes this type"));
375 return NS_OK;
381 // Fourth 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
393 // the context.
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) {
399 if (NS_FAILED(rv)) {
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"));
403 request->Cancel(rv);
404 } else {
405 LOG((" Content handler taking over load"));
406 mUsedContentHandler = true;
409 return rv;
412 } else {
413 LOG(
414 (" DONT_RETARGET flag set, so skipped over random other content "
415 "listeners and content handlers"));
419 // Fifth step: If no listener prefers this type, see if any stream
420 // converters exist to transform this content type into
421 // some other.
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)) {
430 return NS_OK;
435 NS_ASSERTION(!m_targetStreamListener,
436 "If we found a listener, why are we not using it?");
438 if (mFlags & nsIURILoader::DONT_RETARGET) {
439 LOG(
440 (" External handling forced or (listener not interested and no "
441 "stream converter exists), and retargeting disallowed -> aborting"));
442 return NS_ERROR_WONT_HANDLE_CONTENT;
445 // Before dispatching to the external helper app service, check for an HTTP
446 // error page. If we got one, we don't want to handle it with a helper app,
447 // really.
448 // The WPT a-download-click-404.html requires us to silently handle this
449 // without displaying an error page, so we just return early here.
450 // See bug 1604308 for discussion around what the ideal behaviour is.
451 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
452 if (httpChannel) {
453 bool requestSucceeded;
454 rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
455 if (NS_FAILED(rv) || !requestSucceeded) {
456 return NS_OK;
460 // Sixth step:
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) {
479 mContentType = APPLICATION_GUESS_FROM_EXT;
480 aChannel->SetContentType(nsLiteralCString(APPLICATION_GUESS_FROM_EXT));
483 rv = TryExternalHelperApp(helperAppService, aChannel);
484 if (NS_FAILED(rv)) {
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");
493 return rv;
496 nsresult nsDocumentOpenInfo::TryExternalHelperApp(
497 nsIExternalHelperAppService* aHelperAppService, nsIChannel* aChannel) {
498 return aHelperAppService->DoContent(mContentType, aChannel, m_originalContext,
499 false, nullptr,
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) {
512 LOG(
513 ("[0x%p] nsDocumentOpenInfo::ConvertData - reached the recursion "
514 "limit!",
515 this));
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!");
523 nsresult rv = NS_OK;
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;
571 nsresult rv = ConvertData(aChannel, m_contentListener, mContentType, anyType);
572 if (NS_FAILED(rv)) {
573 m_targetStreamListener = nullptr;
574 } else if (m_targetStreamListener) {
575 // We found a converter for this MIME type. We'll just pump data into
576 // it and let the downstream nsDocumentOpenInfo handle things.
577 LOG((" Converter taking over now"));
579 return rv;
582 bool nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
583 nsIChannel* aChannel) {
584 LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x", this,
585 mFlags));
587 MOZ_ASSERT(aListener, "Must have a non-null listener");
588 MOZ_ASSERT(aChannel, "Must have a channel");
590 bool listenerWantsContent = false;
591 nsCString typeToUse;
593 if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) {
594 aListener->IsPreferred(mContentType.get(), getter_Copies(typeToUse),
595 &listenerWantsContent);
596 } else {
597 aListener->CanHandleContent(mContentType.get(), false,
598 getter_Copies(typeToUse),
599 &listenerWantsContent);
601 if (!listenerWantsContent) {
602 LOG((" Listener is not interested"));
603 return false;
606 if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
607 // Need to do a conversion here.
609 nsresult rv = NS_ERROR_NOT_AVAILABLE;
610 if (mAllowListenerConversions) {
611 rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
614 if (NS_FAILED(rv)) {
615 // No conversion path -- we don't want this listener, if we got one
616 m_targetStreamListener = nullptr;
619 LOG((" Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
621 // m_targetStreamListener is now the input end of the converter, and we can
622 // just pump the data in there, if it exists. If it does not, we need to
623 // try other nsIURIContentListeners.
624 return m_targetStreamListener != nullptr;
627 // At this point, aListener wants data of type mContentType. Let 'em have
628 // it. But first, if we are retargeting, set an appropriate flag on the
629 // channel
630 nsLoadFlags loadFlags = 0;
631 aChannel->GetLoadFlags(&loadFlags);
633 // Set this flag to indicate that the channel has been targeted at a final
634 // consumer. This load flag is tested in nsDocLoader::OnProgress.
635 nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED;
637 nsCOMPtr<nsIURIContentListener> originalListener =
638 do_GetInterface(m_originalContext);
639 if (originalListener != aListener) {
640 newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
642 aChannel->SetLoadFlags(loadFlags | newLoadFlags);
644 bool abort = false;
645 bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
646 nsresult rv =
647 aListener->DoContent(mContentType, isPreferred, aChannel,
648 getter_AddRefs(m_targetStreamListener), &abort);
650 if (NS_FAILED(rv)) {
651 LOG_ERROR((" DoContent failed"));
653 // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
654 aChannel->SetLoadFlags(loadFlags);
655 m_targetStreamListener = nullptr;
656 return false;
659 if (abort) {
660 // Nothing else to do here -- aListener is handling it all. Make
661 // sure m_targetStreamListener is null so we don't do anything
662 // after this point.
663 LOG((" Listener has aborted the load"));
664 m_targetStreamListener = nullptr;
667 NS_ASSERTION(abort || m_targetStreamListener,
668 "DoContent returned no listener?");
670 // aListener is handling the load from this point on.
671 return true;
674 bool nsDocumentOpenInfo::TryDefaultContentListener(nsIChannel* aChannel) {
675 if (m_contentListener) {
676 return TryContentListener(m_contentListener, aChannel);
678 return false;
681 ///////////////////////////////////////////////////////////////////////////////////////////////
682 // Implementation of nsURILoader
683 ///////////////////////////////////////////////////////////////////////////////////////////////
685 nsURILoader::nsURILoader() {}
687 nsURILoader::~nsURILoader() {}
689 NS_IMPL_ADDREF(nsURILoader)
690 NS_IMPL_RELEASE(nsURILoader)
692 NS_INTERFACE_MAP_BEGIN(nsURILoader)
693 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader)
694 NS_INTERFACE_MAP_ENTRY(nsIURILoader)
695 NS_INTERFACE_MAP_END
697 NS_IMETHODIMP nsURILoader::RegisterContentListener(
698 nsIURIContentListener* aContentListener) {
699 nsresult rv = NS_OK;
701 nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
702 NS_ASSERTION(weakListener,
703 "your URIContentListener must support weak refs!\n");
705 if (weakListener) m_listeners.AppendObject(weakListener);
707 return rv;
710 NS_IMETHODIMP nsURILoader::UnRegisterContentListener(
711 nsIURIContentListener* aContentListener) {
712 nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
713 if (weakListener) m_listeners.RemoveObject(weakListener);
715 return NS_OK;
718 NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel* channel, uint32_t aFlags,
719 nsIInterfaceRequestor* aWindowContext) {
720 NS_ENSURE_ARG_POINTER(channel);
722 if (LOG_ENABLED()) {
723 nsCOMPtr<nsIURI> uri;
724 channel->GetURI(getter_AddRefs(uri));
725 nsAutoCString spec;
726 uri->GetAsciiSpec(spec);
727 LOG(("nsURILoader::OpenURI for %s", spec.get()));
730 nsCOMPtr<nsIStreamListener> loader;
731 nsresult rv = OpenChannel(channel, aFlags, aWindowContext, false,
732 getter_AddRefs(loader));
733 if (NS_FAILED(rv)) {
734 if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {
735 // Not really an error, from this method's point of view
736 return NS_OK;
740 // This method is not complete. Eventually, we should first go
741 // to the content listener and ask them for a protocol handler...
742 // if they don't give us one, we need to go to the registry and get
743 // the preferred protocol handler.
745 // But for now, I'm going to let necko do the work for us....
746 rv = channel->AsyncOpen(loader);
748 // no content from this load - that's OK.
749 if (rv == NS_ERROR_NO_CONTENT) {
750 LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
751 return NS_OK;
753 return rv;
756 nsresult nsURILoader::OpenChannel(nsIChannel* channel, uint32_t aFlags,
757 nsIInterfaceRequestor* aWindowContext,
758 bool aChannelIsOpen,
759 nsIStreamListener** aListener) {
760 NS_ASSERTION(channel, "Trying to open a null channel!");
761 NS_ASSERTION(aWindowContext, "Window context must not be null");
763 if (LOG_ENABLED()) {
764 nsCOMPtr<nsIURI> uri;
765 channel->GetURI(getter_AddRefs(uri));
766 nsAutoCString spec;
767 uri->GetAsciiSpec(spec);
768 LOG(("nsURILoader::OpenChannel for %s", spec.get()));
771 // we need to create a DocumentOpenInfo object which will go ahead and open
772 // the url and discover the content type....
773 RefPtr<nsDocumentOpenInfo> loader =
774 new nsDocumentOpenInfo(aWindowContext, aFlags, this);
776 // Set the correct loadgroup on the channel
777 nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext));
779 if (!loadGroup) {
780 // XXXbz This context is violating what we'd like to be the new uriloader
781 // api.... Set up a nsDocLoader to handle the loadgroup for this context.
782 // This really needs to go away!
783 nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext));
784 if (listener) {
785 nsCOMPtr<nsISupports> cookie;
786 listener->GetLoadCookie(getter_AddRefs(cookie));
787 if (!cookie) {
788 RefPtr<nsDocLoader> newDocLoader = new nsDocLoader();
789 nsresult rv = newDocLoader->Init();
790 if (NS_FAILED(rv)) return rv;
791 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader);
792 if (NS_FAILED(rv)) return rv;
793 cookie = nsDocLoader::GetAsSupports(newDocLoader);
794 listener->SetLoadCookie(cookie);
796 loadGroup = do_GetInterface(cookie);
800 // If the channel is pending, then we need to remove it from its current
801 // loadgroup
802 nsCOMPtr<nsILoadGroup> oldGroup;
803 channel->GetLoadGroup(getter_AddRefs(oldGroup));
804 if (aChannelIsOpen && !SameCOMIdentity(oldGroup, loadGroup)) {
805 // It is important to add the channel to the new group before
806 // removing it from the old one, so that the load isn't considered
807 // done as soon as the request is removed.
808 loadGroup->AddRequest(channel, nullptr);
810 if (oldGroup) {
811 oldGroup->RemoveRequest(channel, nullptr, NS_BINDING_RETARGETED);
815 channel->SetLoadGroup(loadGroup);
817 // prepare the loader for receiving data
818 nsresult rv = loader->Prepare();
819 if (NS_SUCCEEDED(rv)) NS_ADDREF(*aListener = loader);
820 return rv;
823 NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel, uint32_t aFlags,
824 nsIInterfaceRequestor* aWindowContext,
825 nsIStreamListener** aListener) {
826 bool pending;
827 if (NS_FAILED(channel->IsPending(&pending))) {
828 pending = false;
831 return OpenChannel(channel, aFlags, aWindowContext, pending, aListener);
834 NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie) {
835 nsresult rv;
836 nsCOMPtr<nsIDocumentLoader> docLoader;
838 NS_ENSURE_ARG_POINTER(aLoadCookie);
840 docLoader = do_GetInterface(aLoadCookie, &rv);
841 if (docLoader) {
842 rv = docLoader->Stop();
844 return rv;