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/. */
6
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 "nsIURI.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"
27
28 #include "nsString.h"
29 #include "nsThreadUtils.h"
30 #include "nsReadableUtils.h"
31 #include "nsError.h"
32
33 #include "nsICategoryManager.h"
34 #include "nsCExternalHandlerService.h"
35
36 #include "nsNetCID.h"
37
38 #include "nsMimeTypes.h"
39
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_dom.h"
46 #include "mozilla/StaticPrefs_general.h"
47 #include "nsContentUtils.h"
48
49 mozilla::LazyLogModule nsURILoader::mLog("URILoader");
50
51 #define LOG(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Debug, args)
52 #define LOG_ERROR(args) \
53 MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Error, args)
54 #define LOG_ENABLED() MOZ_LOG_TEST(nsURILoader::mLog, mozilla::LogLevel::Debug)
55
56 NS_IMPL_ADDREF(nsDocumentOpenInfo)
NS_IMPL_RELEASE(nsDocumentOpenInfo)57 NS_IMPL_RELEASE(nsDocumentOpenInfo)
58
59 NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
60 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
61 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
62 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
63 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
64 NS_INTERFACE_MAP_END
65
66 nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
67 uint32_t aFlags, nsURILoader* aURILoader)
68 : m_originalContext(aWindowContext),
69 mFlags(aFlags),
70 mURILoader(aURILoader),
71 mDataConversionDepthLimit(
72 StaticPrefs::general_document_open_conversion_depth_limit()) {}
73
nsDocumentOpenInfo(uint32_t aFlags,bool aAllowListenerConversions)74 nsDocumentOpenInfo::nsDocumentOpenInfo(uint32_t aFlags,
75 bool aAllowListenerConversions)
76 : m_originalContext(nullptr),
77 mFlags(aFlags),
78 mURILoader(nullptr),
79 mDataConversionDepthLimit(
80 StaticPrefs::general_document_open_conversion_depth_limit()),
81 mAllowListenerConversions(aAllowListenerConversions) {}
82
~nsDocumentOpenInfo()83 nsDocumentOpenInfo::~nsDocumentOpenInfo() {}
84
Prepare()85 nsresult nsDocumentOpenInfo::Prepare() {
86 LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
87
88 nsresult rv;
89
90 // ask our window context if it has a uri content listener...
91 m_contentListener = do_GetInterface(m_originalContext, &rv);
92 return rv;
93 }
94
OnStartRequest(nsIRequest * request)95 NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest* request) {
96 LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
97 MOZ_ASSERT(request);
98 if (!request) {
99 return NS_ERROR_UNEXPECTED;
100 }
101
102 nsresult rv = NS_OK;
103
104 //
105 // Deal with "special" HTTP responses:
106 //
107 // - In the case of a 204 (No Content) or 205 (Reset Content) response, do
108 // not try to find a content handler. Return NS_BINDING_ABORTED to cancel
109 // the request. This has the effect of ensuring that the DocLoader does
110 // not try to interpret this as a real request.
111 //
112 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request, &rv));
113
114 if (NS_SUCCEEDED(rv)) {
115 uint32_t responseCode = 0;
116
117 rv = httpChannel->GetResponseStatus(&responseCode);
118
119 if (NS_FAILED(rv)) {
120 LOG_ERROR((" Failed to get HTTP response status"));
121
122 // behave as in the canceled case
123 return NS_OK;
124 }
125
126 LOG((" HTTP response status: %d", responseCode));
127
128 if (204 == responseCode || 205 == responseCode) {
129 return NS_BINDING_ABORTED;
130 }
131 }
132
133 //
134 // Make sure that the transaction has succeeded, so far...
135 //
136 nsresult status;
137
138 rv = request->GetStatus(&status);
139
140 NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get request status!");
141 if (NS_FAILED(rv)) return rv;
142
143 if (NS_FAILED(status)) {
144 LOG_ERROR((" Request failed, status: 0x%08" PRIX32,
145 static_cast<uint32_t>(status)));
146
147 //
148 // The transaction has already reported an error - so it will be torn
149 // down. Therefore, it is not necessary to return an error code...
150 //
151 return NS_OK;
152 }
153
154 rv = DispatchContent(request, nullptr);
155
156 LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08" PRIX32,
157 m_targetStreamListener.get(), static_cast<uint32_t>(rv)));
158
159 NS_ASSERTION(
160 NS_SUCCEEDED(rv) || !m_targetStreamListener,
161 "Must not have an m_targetStreamListener with a failure return!");
162
163 NS_ENSURE_SUCCESS(rv, rv);
164
165 if (m_targetStreamListener)
166 rv = m_targetStreamListener->OnStartRequest(request);
167
168 LOG((" OnStartRequest returning: 0x%08" PRIX32, static_cast<uint32_t>(rv)));
169
170 return rv;
171 }
172
173 NS_IMETHODIMP
CheckListenerChain()174 nsDocumentOpenInfo::CheckListenerChain() {
175 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
176 nsresult rv = NS_OK;
177 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
178 do_QueryInterface(m_targetStreamListener, &rv);
179 if (retargetableListener) {
180 rv = retargetableListener->CheckListenerChain();
181 }
182 LOG(
183 ("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv "
184 "%" PRIx32,
185 this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
186 (nsIStreamListener*)m_targetStreamListener, static_cast<uint32_t>(rv)));
187 return rv;
188 }
189
190 NS_IMETHODIMP
OnDataAvailable(nsIRequest * request,nsIInputStream * inStr,uint64_t sourceOffset,uint32_t count)191 nsDocumentOpenInfo::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr,
192 uint64_t sourceOffset, uint32_t count) {
193 // if we have retarged to the end stream listener, then forward the call....
194 // otherwise, don't do anything
195
196 nsresult rv = NS_OK;
197
198 if (m_targetStreamListener)
199 rv = m_targetStreamListener->OnDataAvailable(request, inStr, sourceOffset,
200 count);
201 return rv;
202 }
203
OnStopRequest(nsIRequest * request,nsresult aStatus)204 NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request,
205 nsresult aStatus) {
206 LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
207
208 if (m_targetStreamListener) {
209 nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
210
211 // If this is a multipart stream, we could get another
212 // OnStartRequest after this... reset state.
213 m_targetStreamListener = nullptr;
214 mContentType.Truncate();
215 listener->OnStopRequest(request, aStatus);
216 }
217 mUsedContentHandler = false;
218
219 // Remember...
220 // In the case of multiplexed streams (such as multipart/x-mixed-replace)
221 // these stream listener methods could be called again :-)
222 //
223 return NS_OK;
224 }
225
DispatchContent(nsIRequest * request,nsISupports * aCtxt)226 nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
227 nsISupports* aCtxt) {
228 LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this,
229 mContentType.get()));
230
231 MOZ_ASSERT(!m_targetStreamListener,
232 "Why do we already have a target stream listener?");
233
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;
239 }
240
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()));
246 }
247
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));
255 }
256
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);
264
265 if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT) {
266 forceExternalHandling = true;
267 }
268
269 LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
270
271 if (!forceExternalHandling) {
272 //
273 // First step: See whether m_contentListener wants to handle this
274 // content type.
275 //
276 if (TryDefaultContentListener(aChannel)) {
277 LOG((" Success! Our default listener likes this type"));
278 // All done here
279 return NS_OK;
280 }
281
282 // If we aren't allowed to try other listeners, just skip through to
283 // trying to convert the data.
284 if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
285 //
286 // Second step: See whether some other registered listener wants
287 // to handle this content type.
288 //
289 int32_t count = mURILoader ? mURILoader->m_listeners.Count() : 0;
290 nsCOMPtr<nsIURIContentListener> listener;
291 for (int32_t i = 0; i < count; i++) {
292 listener = do_QueryReferent(mURILoader->m_listeners[i]);
293 if (listener) {
294 if (TryContentListener(listener, aChannel)) {
295 LOG((" Found listener registered on the URILoader"));
296 return NS_OK;
297 }
298 } else {
299 // remove from the listener list, reset i and update count
300 mURILoader->m_listeners.RemoveObjectAt(i--);
301 --count;
302 }
303 }
304
305 //
306 // Third step: Try to find a content listener that has not yet had
307 // the chance to register, as it is contained in a not-yet-loaded
308 // module, but which has registered a contract ID.
309 //
310 nsCOMPtr<nsICategoryManager> catman =
311 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
312 if (catman) {
313 nsCString contractidString;
314 rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
315 mContentType, contractidString);
316 if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
317 LOG((" Listener contractid for '%s' is '%s'", mContentType.get(),
318 contractidString.get()));
319
320 listener = do_CreateInstance(contractidString.get());
321 LOG((" Listener from category manager: 0x%p", listener.get()));
322
323 if (listener && TryContentListener(listener, aChannel)) {
324 LOG((" Listener from category manager likes this type"));
325 return NS_OK;
326 }
327 }
328 }
329
330 //
331 // Fourth step: try to find an nsIContentHandler for our type.
332 //
333 nsAutoCString handlerContractID(NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
334 handlerContractID += mContentType;
335
336 nsCOMPtr<nsIContentHandler> contentHandler =
337 do_CreateInstance(handlerContractID.get());
338 if (contentHandler) {
339 LOG((" Content handler found"));
340 // Note that m_originalContext can be nullptr when running this in
341 // the parent process on behalf on a docshell in the content process,
342 // and in that case we only support content handlers that don't need
343 // the context.
344 rv = contentHandler->HandleContent(mContentType.get(),
345 m_originalContext, request);
346 // XXXbz returning an error code to represent handling the
347 // content is just bizarre!
348 if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
349 if (NS_FAILED(rv)) {
350 // The content handler has unexpectedly failed. Cancel the request
351 // just in case the handler didn't...
352 LOG((" Content handler failed. Aborting load"));
353 request->Cancel(rv);
354 } else {
355 LOG((" Content handler taking over load"));
356 mUsedContentHandler = true;
357 }
358
359 return rv;
360 }
361 }
362 } else {
363 LOG(
364 (" DONT_RETARGET flag set, so skipped over random other content "
365 "listeners and content handlers"));
366 }
367
368 //
369 // Fifth step: If no listener prefers this type, see if any stream
370 // converters exist to transform this content type into
371 // some other.
372 //
373 // Don't do this if the server sent us a MIME type of "*/*" because they saw
374 // it in our Accept header and got confused.
375 // XXXbz have to be careful here; may end up in some sort of bizarre
376 // infinite decoding loop.
377 if (mContentType != anyType) {
378 rv = TryStreamConversion(aChannel);
379 if (NS_SUCCEEDED(rv)) {
380 return NS_OK;
381 }
382 }
383 }
384
385 NS_ASSERTION(!m_targetStreamListener,
386 "If we found a listener, why are we not using it?");
387
388 if (mFlags & nsIURILoader::DONT_RETARGET) {
389 LOG(
390 (" External handling forced or (listener not interested and no "
391 "stream converter exists), and retargeting disallowed -> aborting"));
392 return NS_ERROR_WONT_HANDLE_CONTENT;
393 }
394
395 // Before dispatching to the external helper app service, check for an HTTP
396 // error page. If we got one, we don't want to handle it with a helper app,
397 // really.
398 // The WPT a-download-click-404.html requires us to silently handle this
399 // without displaying an error page, so we just return early here.
400 // See bug 1604308 for discussion around what the ideal behaviour is.
401 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
402 if (httpChannel) {
403 bool requestSucceeded;
404 rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
405 if (NS_FAILED(rv) || !requestSucceeded) {
406 return NS_OK;
407 }
408 }
409
410 // Sixth step:
411 //
412 // All attempts to dispatch this content have failed. Just pass it off to
413 // the helper app service.
414 //
415
416 nsCOMPtr<nsIExternalHelperAppService> helperAppService =
417 do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
418 if (helperAppService) {
419 LOG((" Passing load off to helper app service"));
420
421 // Set these flags to indicate that the channel has been targeted and that
422 // we are not using the original consumer.
423 nsLoadFlags loadFlags = 0;
424 request->GetLoadFlags(&loadFlags);
425 request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI |
426 nsIChannel::LOAD_TARGETED);
427
428 if (isGuessFromExt) {
429 mContentType = APPLICATION_GUESS_FROM_EXT;
430 aChannel->SetContentType(nsLiteralCString(APPLICATION_GUESS_FROM_EXT));
431 }
432
433 rv = TryExternalHelperApp(helperAppService, aChannel);
434 if (NS_FAILED(rv)) {
435 request->SetLoadFlags(loadFlags);
436 m_targetStreamListener = nullptr;
437 }
438 }
439
440 NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
441 "There is no way we should be successful at this point without "
442 "a m_targetStreamListener");
443 return rv;
444 }
445
TryExternalHelperApp(nsIExternalHelperAppService * aHelperAppService,nsIChannel * aChannel)446 nsresult nsDocumentOpenInfo::TryExternalHelperApp(
447 nsIExternalHelperAppService* aHelperAppService, nsIChannel* aChannel) {
448 return aHelperAppService->DoContent(mContentType, aChannel, m_originalContext,
449 false, nullptr,
450 getter_AddRefs(m_targetStreamListener));
451 }
452
ConvertData(nsIRequest * request,nsIURIContentListener * aListener,const nsACString & aSrcContentType,const nsACString & aOutContentType)453 nsresult nsDocumentOpenInfo::ConvertData(nsIRequest* request,
454 nsIURIContentListener* aListener,
455 const nsACString& aSrcContentType,
456 const nsACString& aOutContentType) {
457 LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
458 PromiseFlatCString(aSrcContentType).get(),
459 PromiseFlatCString(aOutContentType).get()));
460
461 if (mDataConversionDepthLimit == 0) {
462 LOG(
463 ("[0x%p] nsDocumentOpenInfo::ConvertData - reached the recursion "
464 "limit!",
465 this));
466 // This will fall back to external helper app handling.
467 return NS_ERROR_ABORT;
468 }
469
470 MOZ_ASSERT(aSrcContentType != aOutContentType,
471 "ConvertData called when the two types are the same!");
472
473 nsresult rv = NS_OK;
474
475 nsCOMPtr<nsIStreamConverterService> StreamConvService =
476 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
477 if (NS_FAILED(rv)) return rv;
478
479 LOG((" Got converter service"));
480
481 // When applying stream decoders, it is necessary to "insert" an
482 // intermediate nsDocumentOpenInfo instance to handle the targeting of
483 // the "final" stream or streams.
484 //
485 // For certain content types (ie. multi-part/x-mixed-replace) the input
486 // stream is split up into multiple destination streams. This
487 // intermediate instance is used to target these "decoded" streams...
488 //
489 RefPtr<nsDocumentOpenInfo> nextLink = Clone();
490
491 LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
492
493 // Decrease the conversion recursion limit by one to prevent infinite loops.
494 nextLink->mDataConversionDepthLimit = mDataConversionDepthLimit - 1;
495
496 // Make sure nextLink starts with the contentListener that said it wanted
497 // the results of this decode.
498 nextLink->m_contentListener = aListener;
499 // Also make sure it has to look for a stream listener to pump data into.
500 nextLink->m_targetStreamListener = nullptr;
501
502 // Make sure that nextLink treats the data as aOutContentType when
503 // dispatching; that way even if the stream converters don't change the type
504 // on the channel we will still do the right thing. If aOutContentType is
505 // */*, that's OK -- that will just indicate to nextLink that it should get
506 // the type off the channel.
507 nextLink->mContentType = aOutContentType;
508
509 // The following call sets m_targetStreamListener to the input end of the
510 // stream converter and sets the output end of the stream converter to
511 // nextLink. As we pump data into m_targetStreamListener the stream
512 // converter will convert it and pass the converted data to nextLink.
513 return StreamConvService->AsyncConvertData(
514 PromiseFlatCString(aSrcContentType).get(),
515 PromiseFlatCString(aOutContentType).get(), nextLink, request,
516 getter_AddRefs(m_targetStreamListener));
517 }
518
TryStreamConversion(nsIChannel * aChannel)519 nsresult nsDocumentOpenInfo::TryStreamConversion(nsIChannel* aChannel) {
520 constexpr auto anyType = "*/*"_ns;
521 nsresult rv = ConvertData(aChannel, m_contentListener, mContentType, anyType);
522 if (NS_FAILED(rv)) {
523 m_targetStreamListener = nullptr;
524 } else if (m_targetStreamListener) {
525 // We found a converter for this MIME type. We'll just pump data into
526 // it and let the downstream nsDocumentOpenInfo handle things.
527 LOG((" Converter taking over now"));
528 }
529 return rv;
530 }
531
TryContentListener(nsIURIContentListener * aListener,nsIChannel * aChannel)532 bool nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
533 nsIChannel* aChannel) {
534 LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x", this,
535 mFlags));
536
537 MOZ_ASSERT(aListener, "Must have a non-null listener");
538 MOZ_ASSERT(aChannel, "Must have a channel");
539
540 bool listenerWantsContent = false;
541 nsCString typeToUse;
542
543 if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) {
544 aListener->IsPreferred(mContentType.get(), getter_Copies(typeToUse),
545 &listenerWantsContent);
546 } else {
547 aListener->CanHandleContent(mContentType.get(), false,
548 getter_Copies(typeToUse),
549 &listenerWantsContent);
550 }
551 if (!listenerWantsContent) {
552 LOG((" Listener is not interested"));
553 return false;
554 }
555
556 if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
557 // Need to do a conversion here.
558
559 nsresult rv = NS_ERROR_NOT_AVAILABLE;
560 if (mAllowListenerConversions) {
561 rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
562 }
563
564 if (NS_FAILED(rv)) {
565 // No conversion path -- we don't want this listener, if we got one
566 m_targetStreamListener = nullptr;
567 }
568
569 LOG((" Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
570
571 // m_targetStreamListener is now the input end of the converter, and we can
572 // just pump the data in there, if it exists. If it does not, we need to
573 // try other nsIURIContentListeners.
574 return m_targetStreamListener != nullptr;
575 }
576
577 // At this point, aListener wants data of type mContentType. Let 'em have
578 // it. But first, if we are retargeting, set an appropriate flag on the
579 // channel
580 nsLoadFlags loadFlags = 0;
581 aChannel->GetLoadFlags(&loadFlags);
582
583 // Set this flag to indicate that the channel has been targeted at a final
584 // consumer. This load flag is tested in nsDocLoader::OnProgress.
585 nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED;
586
587 nsCOMPtr<nsIURIContentListener> originalListener =
588 do_GetInterface(m_originalContext);
589 if (originalListener != aListener) {
590 newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
591 }
592 aChannel->SetLoadFlags(loadFlags | newLoadFlags);
593
594 bool abort = false;
595 bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
596 nsresult rv =
597 aListener->DoContent(mContentType, isPreferred, aChannel,
598 getter_AddRefs(m_targetStreamListener), &abort);
599
600 if (NS_FAILED(rv)) {
601 LOG_ERROR((" DoContent failed"));
602
603 // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
604 aChannel->SetLoadFlags(loadFlags);
605 m_targetStreamListener = nullptr;
606 return false;
607 }
608
609 if (abort) {
610 // Nothing else to do here -- aListener is handling it all. Make
611 // sure m_targetStreamListener is null so we don't do anything
612 // after this point.
613 LOG((" Listener has aborted the load"));
614 m_targetStreamListener = nullptr;
615 }
616
617 NS_ASSERTION(abort || m_targetStreamListener,
618 "DoContent returned no listener?");
619
620 // aListener is handling the load from this point on.
621 return true;
622 }
623
TryDefaultContentListener(nsIChannel * aChannel)624 bool nsDocumentOpenInfo::TryDefaultContentListener(nsIChannel* aChannel) {
625 if (m_contentListener) {
626 return TryContentListener(m_contentListener, aChannel);
627 }
628 return false;
629 }
630
631 ///////////////////////////////////////////////////////////////////////////////////////////////
632 // Implementation of nsURILoader
633 ///////////////////////////////////////////////////////////////////////////////////////////////
634
nsURILoader()635 nsURILoader::nsURILoader() {}
636
~nsURILoader()637 nsURILoader::~nsURILoader() {}
638
639 NS_IMPL_ADDREF(nsURILoader)
NS_IMPL_RELEASE(nsURILoader)640 NS_IMPL_RELEASE(nsURILoader)
641
642 NS_INTERFACE_MAP_BEGIN(nsURILoader)
643 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader)
644 NS_INTERFACE_MAP_ENTRY(nsIURILoader)
645 NS_INTERFACE_MAP_END
646
647 NS_IMETHODIMP nsURILoader::RegisterContentListener(
648 nsIURIContentListener* aContentListener) {
649 nsresult rv = NS_OK;
650
651 nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
652 NS_ASSERTION(weakListener,
653 "your URIContentListener must support weak refs!\n");
654
655 if (weakListener) m_listeners.AppendObject(weakListener);
656
657 return rv;
658 }
659
UnRegisterContentListener(nsIURIContentListener * aContentListener)660 NS_IMETHODIMP nsURILoader::UnRegisterContentListener(
661 nsIURIContentListener* aContentListener) {
662 nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
663 if (weakListener) m_listeners.RemoveObject(weakListener);
664
665 return NS_OK;
666 }
667
OpenURI(nsIChannel * channel,uint32_t aFlags,nsIInterfaceRequestor * aWindowContext)668 NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel* channel, uint32_t aFlags,
669 nsIInterfaceRequestor* aWindowContext) {
670 NS_ENSURE_ARG_POINTER(channel);
671
672 if (LOG_ENABLED()) {
673 nsCOMPtr<nsIURI> uri;
674 channel->GetURI(getter_AddRefs(uri));
675 nsAutoCString spec;
676 uri->GetAsciiSpec(spec);
677 LOG(("nsURILoader::OpenURI for %s", spec.get()));
678 }
679
680 nsCOMPtr<nsIStreamListener> loader;
681 nsresult rv = OpenChannel(channel, aFlags, aWindowContext, false,
682 getter_AddRefs(loader));
683 if (NS_FAILED(rv)) {
684 if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {
685 // Not really an error, from this method's point of view
686 return NS_OK;
687 }
688 }
689
690 // This method is not complete. Eventually, we should first go
691 // to the content listener and ask them for a protocol handler...
692 // if they don't give us one, we need to go to the registry and get
693 // the preferred protocol handler.
694
695 // But for now, I'm going to let necko do the work for us....
696 rv = channel->AsyncOpen(loader);
697
698 // no content from this load - that's OK.
699 if (rv == NS_ERROR_NO_CONTENT) {
700 LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
701 return NS_OK;
702 }
703 return rv;
704 }
705
OpenChannel(nsIChannel * channel,uint32_t aFlags,nsIInterfaceRequestor * aWindowContext,bool aChannelIsOpen,nsIStreamListener ** aListener)706 nsresult nsURILoader::OpenChannel(nsIChannel* channel, uint32_t aFlags,
707 nsIInterfaceRequestor* aWindowContext,
708 bool aChannelIsOpen,
709 nsIStreamListener** aListener) {
710 NS_ASSERTION(channel, "Trying to open a null channel!");
711 NS_ASSERTION(aWindowContext, "Window context must not be null");
712
713 if (LOG_ENABLED()) {
714 nsCOMPtr<nsIURI> uri;
715 channel->GetURI(getter_AddRefs(uri));
716 nsAutoCString spec;
717 uri->GetAsciiSpec(spec);
718 LOG(("nsURILoader::OpenChannel for %s", spec.get()));
719 }
720
721 // we need to create a DocumentOpenInfo object which will go ahead and open
722 // the url and discover the content type....
723 RefPtr<nsDocumentOpenInfo> loader =
724 new nsDocumentOpenInfo(aWindowContext, aFlags, this);
725
726 // Set the correct loadgroup on the channel
727 nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext));
728
729 if (!loadGroup) {
730 // XXXbz This context is violating what we'd like to be the new uriloader
731 // api.... Set up a nsDocLoader to handle the loadgroup for this context.
732 // This really needs to go away!
733 nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext));
734 if (listener) {
735 nsCOMPtr<nsISupports> cookie;
736 listener->GetLoadCookie(getter_AddRefs(cookie));
737 if (!cookie) {
738 RefPtr<nsDocLoader> newDocLoader = new nsDocLoader();
739 nsresult rv = newDocLoader->Init();
740 if (NS_FAILED(rv)) return rv;
741 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader);
742 if (NS_FAILED(rv)) return rv;
743 cookie = nsDocLoader::GetAsSupports(newDocLoader);
744 listener->SetLoadCookie(cookie);
745 }
746 loadGroup = do_GetInterface(cookie);
747 }
748 }
749
750 // If the channel is pending, then we need to remove it from its current
751 // loadgroup
752 nsCOMPtr<nsILoadGroup> oldGroup;
753 channel->GetLoadGroup(getter_AddRefs(oldGroup));
754 if (aChannelIsOpen && !SameCOMIdentity(oldGroup, loadGroup)) {
755 // It is important to add the channel to the new group before
756 // removing it from the old one, so that the load isn't considered
757 // done as soon as the request is removed.
758 loadGroup->AddRequest(channel, nullptr);
759
760 if (oldGroup) {
761 oldGroup->RemoveRequest(channel, nullptr, NS_BINDING_RETARGETED);
762 }
763 }
764
765 channel->SetLoadGroup(loadGroup);
766
767 // prepare the loader for receiving data
768 nsresult rv = loader->Prepare();
769 if (NS_SUCCEEDED(rv)) NS_ADDREF(*aListener = loader);
770 return rv;
771 }
772
OpenChannel(nsIChannel * channel,uint32_t aFlags,nsIInterfaceRequestor * aWindowContext,nsIStreamListener ** aListener)773 NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel, uint32_t aFlags,
774 nsIInterfaceRequestor* aWindowContext,
775 nsIStreamListener** aListener) {
776 bool pending;
777 if (NS_FAILED(channel->IsPending(&pending))) {
778 pending = false;
779 }
780
781 return OpenChannel(channel, aFlags, aWindowContext, pending, aListener);
782 }
783
Stop(nsISupports * aLoadCookie)784 NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie) {
785 nsresult rv;
786 nsCOMPtr<nsIDocumentLoader> docLoader;
787
788 NS_ENSURE_ARG_POINTER(aLoadCookie);
789
790 docLoader = do_GetInterface(aLoadCookie, &rv);
791 if (docLoader) {
792 rv = docLoader->Stop();
793 }
794 return rv;
795 }
796