uriloader/base/nsURILoader.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:0c96959264e7
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 "nsAutoPtr.h"
9 #include "nsIURIContentListener.h"
10 #include "nsIContentHandler.h"
11 #include "nsILoadGroup.h"
12 #include "nsIDocumentLoader.h"
13 #include "nsIWebProgress.h"
14 #include "nsIWebProgressListener.h"
15 #include "nsIIOService.h"
16 #include "nsIServiceManager.h"
17 #include "nsIStreamListener.h"
18 #include "nsIURI.h"
19 #include "nsIChannel.h"
20 #include "nsIInterfaceRequestor.h"
21 #include "nsIInterfaceRequestorUtils.h"
22 #include "nsIProgressEventSink.h"
23 #include "nsIInputStream.h"
24 #include "nsIStreamConverterService.h"
25 #include "nsWeakReference.h"
26 #include "nsIHttpChannel.h"
27 #include "nsIMultiPartChannel.h"
28 #include "netCore.h"
29 #include "nsCRT.h"
30 #include "nsIDocShell.h"
31 #include "nsIDocShellTreeItem.h"
32 #include "nsIDocShellTreeOwner.h"
33 #include "nsIThreadRetargetableStreamListener.h"
34
35 #include "nsXPIDLString.h"
36 #include "nsString.h"
37 #include "nsNetUtil.h"
38 #include "nsThreadUtils.h"
39 #include "nsReadableUtils.h"
40 #include "nsError.h"
41
42 #include "nsICategoryManager.h"
43 #include "nsCExternalHandlerService.h" // contains contractids for the helper app service
44
45 #include "nsIMIMEHeaderParam.h"
46 #include "nsNetCID.h"
47
48 #include "nsMimeTypes.h"
49
50 #include "nsDocLoader.h"
51 #include "mozilla/Attributes.h"
52 #include "mozilla/Preferences.h"
53
54 #ifdef PR_LOGGING
55 PRLogModuleInfo* nsURILoader::mLog = nullptr;
56 #endif
57
58 #define LOG(args) PR_LOG(nsURILoader::mLog, PR_LOG_DEBUG, args)
59 #define LOG_ERROR(args) PR_LOG(nsURILoader::mLog, PR_LOG_ERROR, args)
60 #define LOG_ENABLED() PR_LOG_TEST(nsURILoader::mLog, PR_LOG_DEBUG)
61
62 #define NS_PREF_DISABLE_BACKGROUND_HANDLING \
63 "security.exthelperapp.disable_background_handling"
64
65 /**
66 * The nsDocumentOpenInfo contains the state required when a single
67 * document is being opened in order to discover the content type...
68 * Each instance remains alive until its target URL has been loaded
69 * (or aborted).
70 */
71 class nsDocumentOpenInfo MOZ_FINAL : public nsIStreamListener
72 , public nsIThreadRetargetableStreamListener
73 {
74 public:
75 // Needed for nsCOMPtr to work right... Don't call this!
76 nsDocumentOpenInfo();
77
78 // Real constructor
79 // aFlags is a combination of the flags on nsIURILoader
80 nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
81 uint32_t aFlags,
82 nsURILoader* aURILoader);
83
84 NS_DECL_THREADSAFE_ISUPPORTS
85
86 /**
87 * Prepares this object for receiving data. The stream
88 * listener methods of this class must not be called before calling this
89 * method.
90 */
91 nsresult Prepare();
92
93 // Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
94 // take the data off our hands.
95 nsresult DispatchContent(nsIRequest *request, nsISupports * aCtxt);
96
97 // Call this if we need to insert a stream converter from aSrcContentType to
98 // aOutContentType into the StreamListener chain. DO NOT call it if the two
99 // types are the same, since no conversion is needed in that case.
100 nsresult ConvertData(nsIRequest *request,
101 nsIURIContentListener *aListener,
102 const nsACString & aSrcContentType,
103 const nsACString & aOutContentType);
104
105 /**
106 * Function to attempt to use aListener to handle the load. If
107 * true is returned, nothing else needs to be done; if false
108 * is returned, then a different way of handling the load should be
109 * tried.
110 */
111 bool TryContentListener(nsIURIContentListener* aListener,
112 nsIChannel* aChannel);
113
114 // nsIRequestObserver methods:
115 NS_DECL_NSIREQUESTOBSERVER
116
117 // nsIStreamListener methods:
118 NS_DECL_NSISTREAMLISTENER
119
120 // nsIThreadRetargetableStreamListener
121 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
122 protected:
123 ~nsDocumentOpenInfo();
124
125 protected:
126 /**
127 * The first content listener to try dispatching data to. Typically
128 * the listener associated with the entity that originated the load.
129 */
130 nsCOMPtr<nsIURIContentListener> m_contentListener;
131
132 /**
133 * The stream listener to forward nsIStreamListener notifications
134 * to. This is set once the load is dispatched.
135 */
136 nsCOMPtr<nsIStreamListener> m_targetStreamListener;
137
138 /**
139 * A pointer to the entity that originated the load. We depend on getting
140 * things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
141 */
142 nsCOMPtr<nsIInterfaceRequestor> m_originalContext;
143
144 /**
145 * IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent
146 * (also determines whether we use CanHandleContent or IsPreferred).
147 * DONT_RETARGET means that we will only try m_originalContext, no other
148 * listeners.
149 */
150 uint32_t mFlags;
151
152 /**
153 * The type of the data we will be trying to dispatch.
154 */
155 nsCString mContentType;
156
157 /**
158 * Reference to the URILoader service so we can access its list of
159 * nsIURIContentListeners.
160 */
161 nsRefPtr<nsURILoader> mURILoader;
162 };
163
164 NS_IMPL_ADDREF(nsDocumentOpenInfo)
165 NS_IMPL_RELEASE(nsDocumentOpenInfo)
166
167 NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
168 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
169 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
170 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
171 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
172 NS_INTERFACE_MAP_END_THREADSAFE
173
174 nsDocumentOpenInfo::nsDocumentOpenInfo()
175 {
176 NS_NOTREACHED("This should never be called\n");
177 }
178
179 nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
180 uint32_t aFlags,
181 nsURILoader* aURILoader)
182 : m_originalContext(aWindowContext),
183 mFlags(aFlags),
184 mURILoader(aURILoader)
185 {
186 }
187
188 nsDocumentOpenInfo::~nsDocumentOpenInfo()
189 {
190 }
191
192 nsresult nsDocumentOpenInfo::Prepare()
193 {
194 LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
195
196 nsresult rv;
197
198 // ask our window context if it has a uri content listener...
199 m_contentListener = do_GetInterface(m_originalContext, &rv);
200 return rv;
201 }
202
203 NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
204 {
205 LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
206 MOZ_ASSERT(request);
207 if (!request) {
208 return NS_ERROR_UNEXPECTED;
209 }
210
211 nsresult rv = NS_OK;
212
213 //
214 // Deal with "special" HTTP responses:
215 //
216 // - In the case of a 204 (No Content) or 205 (Reset Content) response, do
217 // not try to find a content handler. Return NS_BINDING_ABORTED to cancel
218 // the request. This has the effect of ensuring that the DocLoader does
219 // not try to interpret this as a real request.
220 //
221 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request, &rv));
222
223 if (NS_SUCCEEDED(rv)) {
224 uint32_t responseCode = 0;
225
226 rv = httpChannel->GetResponseStatus(&responseCode);
227
228 if (NS_FAILED(rv)) {
229 LOG_ERROR((" Failed to get HTTP response status"));
230
231 // behave as in the canceled case
232 return NS_OK;
233 }
234
235 LOG((" HTTP response status: %d", responseCode));
236
237 if (204 == responseCode || 205 == responseCode) {
238 return NS_BINDING_ABORTED;
239 }
240 }
241
242 //
243 // Make sure that the transaction has succeeded, so far...
244 //
245 nsresult status;
246
247 rv = request->GetStatus(&status);
248
249 NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get request status!");
250 if (NS_FAILED(rv)) return rv;
251
252 if (NS_FAILED(status)) {
253 LOG_ERROR((" Request failed, status: 0x%08X", rv));
254
255 //
256 // The transaction has already reported an error - so it will be torn
257 // down. Therefore, it is not necessary to return an error code...
258 //
259 return NS_OK;
260 }
261
262 rv = DispatchContent(request, aCtxt);
263
264 LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08X", m_targetStreamListener.get(), rv));
265
266 NS_ASSERTION(NS_SUCCEEDED(rv) || !m_targetStreamListener,
267 "Must not have an m_targetStreamListener with a failure return!");
268
269 NS_ENSURE_SUCCESS(rv, rv);
270
271 if (m_targetStreamListener)
272 rv = m_targetStreamListener->OnStartRequest(request, aCtxt);
273
274 LOG((" OnStartRequest returning: 0x%08X", rv));
275
276 return rv;
277 }
278
279 NS_IMETHODIMP
280 nsDocumentOpenInfo::CheckListenerChain()
281 {
282 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
283 nsresult rv = NS_OK;
284 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
285 do_QueryInterface(m_targetStreamListener, &rv);
286 if (retargetableListener) {
287 rv = retargetableListener->CheckListenerChain();
288 }
289 LOG(("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv %x",
290 this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
291 (nsIStreamListener*)m_targetStreamListener, rv));
292 return rv;
293 }
294
295 NS_IMETHODIMP
296 nsDocumentOpenInfo::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
297 nsIInputStream * inStr,
298 uint64_t sourceOffset, uint32_t count)
299 {
300 // if we have retarged to the end stream listener, then forward the call....
301 // otherwise, don't do anything
302
303 nsresult rv = NS_OK;
304
305 if (m_targetStreamListener)
306 rv = m_targetStreamListener->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count);
307 return rv;
308 }
309
310 NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest *request, nsISupports *aCtxt,
311 nsresult aStatus)
312 {
313 LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
314
315 if ( m_targetStreamListener)
316 {
317 nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
318
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);
324 }
325
326 // Remember...
327 // In the case of multiplexed streams (such as multipart/x-mixed-replace)
328 // these stream listener methods could be called again :-)
329 //
330 return NS_OK;
331 }
332
333 nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports * aCtxt)
334 {
335 LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this, mContentType.get()));
336
337 NS_PRECONDITION(!m_targetStreamListener,
338 "Why do we already have a target stream listener?");
339
340 nsresult rv;
341 nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
342 if (!aChannel) {
343 LOG_ERROR((" Request is not a channel. Bailing."));
344 return NS_ERROR_FAILURE;
345 }
346
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()));
352 }
353
354 bool 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));
361 }
362
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
366 // type.
367 bool forceExternalHandling = false;
368 uint32_t disposition;
369 rv = aChannel->GetContentDisposition(&disposition);
370 if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT)
371 forceExternalHandling = true;
372
373 LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
374
375 // We're going to try to find a contentListener that can handle our data
376 nsCOMPtr<nsIURIContentListener> contentListener;
377 // The type or data the contentListener wants.
378 nsXPIDLCString desiredContentType;
379
380 if (!forceExternalHandling)
381 {
382 //
383 // First step: See whether m_contentListener wants to handle this
384 // content type.
385 //
386 if (m_contentListener && TryContentListener(m_contentListener, aChannel)) {
387 LOG((" Success! Our default listener likes this type"));
388 // All done here
389 return NS_OK;
390 }
391
392 // If we aren't allowed to try other listeners, just skip through to
393 // trying to convert the data.
394 if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
395
396 //
397 // Second step: See whether some other registered listener wants
398 // to handle this content type.
399 //
400 int32_t count = mURILoader->m_listeners.Count();
401 nsCOMPtr<nsIURIContentListener> listener;
402 for (int32_t i = 0; i < count; i++) {
403 listener = do_QueryReferent(mURILoader->m_listeners[i]);
404 if (listener) {
405 if (TryContentListener(listener, aChannel)) {
406 LOG((" Found listener registered on the URILoader"));
407 return NS_OK;
408 }
409 } else {
410 // remove from the listener list, reset i and update count
411 mURILoader->m_listeners.RemoveObjectAt(i--);
412 --count;
413 }
414 }
415
416 //
417 // Third step: Try to find a content listener that has not yet had
418 // the chance to register, as it is contained in a not-yet-loaded
419 // module, but which has registered a contract ID.
420 //
421 nsCOMPtr<nsICategoryManager> catman =
422 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
423 if (catman) {
424 nsXPIDLCString contractidString;
425 rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
426 mContentType.get(),
427 getter_Copies(contractidString));
428 if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
429 LOG((" Listener contractid for '%s' is '%s'",
430 mContentType.get(), contractidString.get()));
431
432 listener = do_CreateInstance(contractidString);
433 LOG((" Listener from category manager: 0x%p", listener.get()));
434
435 if (listener && TryContentListener(listener, aChannel)) {
436 LOG((" Listener from category manager likes this type"));
437 return NS_OK;
438 }
439 }
440 }
441
442 //
443 // Fourth step: try to find an nsIContentHandler for our type.
444 //
445 nsAutoCString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
446 handlerContractID += mContentType;
447
448 nsCOMPtr<nsIContentHandler> contentHandler =
449 do_CreateInstance(handlerContractID.get());
450 if (contentHandler) {
451 LOG((" Content handler found"));
452 rv = contentHandler->HandleContent(mContentType.get(),
453 m_originalContext, request);
454 // XXXbz returning an error code to represent handling the
455 // content is just bizarre!
456 if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
457 if (NS_FAILED(rv)) {
458 // The content handler has unexpectedly failed. Cancel the request
459 // just in case the handler didn't...
460 LOG((" Content handler failed. Aborting load"));
461 request->Cancel(rv);
462 }
463 #ifdef PR_LOGGING
464 else {
465 LOG((" Content handler taking over load"));
466 }
467 #endif
468
469 return rv;
470 }
471 }
472 } else {
473 LOG((" DONT_RETARGET flag set, so skipped over random other content "
474 "listeners and content handlers"));
475 }
476
477 //
478 // Fifth step: If no listener prefers this type, see if any stream
479 // converters exist to transform this content type into
480 // some other.
481 //
482 // Don't do this if the server sent us a MIME type of "*/*" because they saw
483 // it in our Accept header and got confused.
484 // XXXbz have to be careful here; may end up in some sort of bizarre infinite
485 // decoding loop.
486 if (mContentType != anyType) {
487 rv = ConvertData(request, m_contentListener, mContentType, anyType);
488 if (NS_FAILED(rv)) {
489 m_targetStreamListener = nullptr;
490 } else if (m_targetStreamListener) {
491 // We found a converter for this MIME type. We'll just pump data into it
492 // and let the downstream nsDocumentOpenInfo handle things.
493 LOG((" Converter taking over now"));
494 return NS_OK;
495 }
496 }
497 }
498
499 NS_ASSERTION(!m_targetStreamListener,
500 "If we found a listener, why are we not using it?");
501
502 if (mFlags & nsIURILoader::DONT_RETARGET) {
503 LOG((" External handling forced or (listener not interested and no "
504 "stream converter exists), and retargeting disallowed -> aborting"));
505 return NS_ERROR_WONT_HANDLE_CONTENT;
506 }
507
508 // Before dispatching to the external helper app service, check for an HTTP
509 // error page. If we got one, we don't want to handle it with a helper app,
510 // really.
511 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
512 if (httpChannel) {
513 bool requestSucceeded;
514 httpChannel->GetRequestSucceeded(&requestSucceeded);
515 if (!requestSucceeded) {
516 // returning error from OnStartRequest will cancel the channel
517 return NS_ERROR_FILE_NOT_FOUND;
518 }
519 }
520
521 // Sixth step:
522 //
523 // All attempts to dispatch this content have failed. Just pass it off to
524 // the helper app service.
525 //
526
527 //
528 // Optionally, we may want to disable background handling by the external
529 // helper application service.
530 //
531 if (mozilla::Preferences::GetBool(NS_PREF_DISABLE_BACKGROUND_HANDLING,
532 false)) {
533 // First, we will ensure that the parent docshell is in an active
534 // state as we will disallow all external application handling unless it is
535 // in the foreground.
536 nsCOMPtr<nsIDocShell> docShell(do_GetInterface(m_originalContext));
537 if (!docShell) {
538 // If we can't perform our security check we definitely don't want to go
539 // any further!
540 LOG(("Failed to get DocShell to ensure it is active before anding off to "
541 "helper app service. Aborting."));
542 return NS_ERROR_FAILURE;
543 }
544
545 // Ensure the DocShell is active before continuing.
546 bool isActive = false;
547 docShell->GetIsActive(&isActive);
548 if (!isActive) {
549 LOG((" Check for active DocShell returned false. Aborting hand off to "
550 "helper app service."));
551 return NS_ERROR_DOM_SECURITY_ERR;
552 }
553 }
554
555 nsCOMPtr<nsIExternalHelperAppService> helperAppService =
556 do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
557 if (helperAppService) {
558 LOG((" Passing load off to helper app service"));
559
560 // Set these flags to indicate that the channel has been targeted and that
561 // we are not using the original consumer.
562 nsLoadFlags loadFlags = 0;
563 request->GetLoadFlags(&loadFlags);
564 request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
565 | nsIChannel::LOAD_TARGETED);
566
567 if (isGuessFromExt) {
568 mContentType = APPLICATION_GUESS_FROM_EXT;
569 aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
570 }
571
572 rv = helperAppService->DoContent(mContentType,
573 request,
574 m_originalContext,
575 false,
576 getter_AddRefs(m_targetStreamListener));
577 if (NS_FAILED(rv)) {
578 request->SetLoadFlags(loadFlags);
579 m_targetStreamListener = nullptr;
580 }
581 }
582
583 NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
584 "There is no way we should be successful at this point without a m_targetStreamListener");
585 return rv;
586 }
587
588 nsresult
589 nsDocumentOpenInfo::ConvertData(nsIRequest *request,
590 nsIURIContentListener* aListener,
591 const nsACString& aSrcContentType,
592 const nsACString& aOutContentType)
593 {
594 LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
595 PromiseFlatCString(aSrcContentType).get(),
596 PromiseFlatCString(aOutContentType).get()));
597
598 NS_PRECONDITION(aSrcContentType != aOutContentType,
599 "ConvertData called when the two types are the same!");
600 nsresult rv = NS_OK;
601
602 nsCOMPtr<nsIStreamConverterService> StreamConvService =
603 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
604 if (NS_FAILED(rv)) return rv;
605
606 LOG((" Got converter service"));
607
608 // When applying stream decoders, it is necessary to "insert" an
609 // intermediate nsDocumentOpenInfo instance to handle the targeting of
610 // the "final" stream or streams.
611 //
612 // For certain content types (ie. multi-part/x-mixed-replace) the input
613 // stream is split up into multiple destination streams. This
614 // intermediate instance is used to target these "decoded" streams...
615 //
616 nsRefPtr<nsDocumentOpenInfo> nextLink =
617 new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
618 if (!nextLink) return NS_ERROR_OUT_OF_MEMORY;
619
620 LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
621
622 // Make sure nextLink starts with the contentListener that said it wanted the
623 // results of this decode.
624 nextLink->m_contentListener = aListener;
625 // Also make sure it has to look for a stream listener to pump data into.
626 nextLink->m_targetStreamListener = nullptr;
627
628 // Make sure that nextLink treats the data as aOutContentType when
629 // dispatching; that way even if the stream converters don't change the type
630 // on the channel we will still do the right thing. If aOutContentType is
631 // */*, that's OK -- that will just indicate to nextLink that it should get
632 // the type off the channel.
633 nextLink->mContentType = aOutContentType;
634
635 // The following call sets m_targetStreamListener to the input end of the
636 // stream converter and sets the output end of the stream converter to
637 // nextLink. As we pump data into m_targetStreamListener the stream
638 // converter will convert it and pass the converted data to nextLink.
639 return StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(),
640 PromiseFlatCString(aOutContentType).get(),
641 nextLink,
642 request,
643 getter_AddRefs(m_targetStreamListener));
644 }
645
646 bool
647 nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
648 nsIChannel* aChannel)
649 {
650 LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x",
651 this, mFlags));
652
653 NS_PRECONDITION(aListener, "Must have a non-null listener");
654 NS_PRECONDITION(aChannel, "Must have a channel");
655
656 bool listenerWantsContent = false;
657 nsXPIDLCString typeToUse;
658
659 if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) {
660 aListener->IsPreferred(mContentType.get(),
661 getter_Copies(typeToUse),
662 &listenerWantsContent);
663 } else {
664 aListener->CanHandleContent(mContentType.get(), false,
665 getter_Copies(typeToUse),
666 &listenerWantsContent);
667 }
668 if (!listenerWantsContent) {
669 LOG((" Listener is not interested"));
670 return false;
671 }
672
673 if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
674 // Need to do a conversion here.
675
676 nsresult rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
677
678 if (NS_FAILED(rv)) {
679 // No conversion path -- we don't want this listener, if we got one
680 m_targetStreamListener = nullptr;
681 }
682
683 LOG((" Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
684
685 // m_targetStreamListener is now the input end of the converter, and we can
686 // just pump the data in there, if it exists. If it does not, we need to
687 // try other nsIURIContentListeners.
688 return m_targetStreamListener != nullptr;
689 }
690
691 // At this point, aListener wants data of type mContentType. Let 'em have
692 // it. But first, if we are retargeting, set an appropriate flag on the
693 // channel
694 nsLoadFlags loadFlags = 0;
695 aChannel->GetLoadFlags(&loadFlags);
696
697 // Set this flag to indicate that the channel has been targeted at a final
698 // consumer. This load flag is tested in nsDocLoader::OnProgress.
699 nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED;
700
701 nsCOMPtr<nsIURIContentListener> originalListener =
702 do_GetInterface(m_originalContext);
703 if (originalListener != aListener) {
704 newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
705 }
706 aChannel->SetLoadFlags(loadFlags | newLoadFlags);
707
708 bool abort = false;
709 bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
710 nsresult rv = aListener->DoContent(mContentType.get(),
711 isPreferred,
712 aChannel,
713 getter_AddRefs(m_targetStreamListener),
714 &abort);
715
716 if (NS_FAILED(rv)) {
717 LOG_ERROR((" DoContent failed"));
718
719 // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
720 aChannel->SetLoadFlags(loadFlags);
721 m_targetStreamListener = nullptr;
722 return false;
723 }
724
725 if (abort) {
726 // Nothing else to do here -- aListener is handling it all. Make
727 // sure m_targetStreamListener is null so we don't do anything
728 // after this point.
729 LOG((" Listener has aborted the load"));
730 m_targetStreamListener = nullptr;
731 }
732
733 NS_ASSERTION(abort || m_targetStreamListener, "DoContent returned no listener?");
734
735 // aListener is handling the load from this point on.
736 return true;
737 }
738
739
740 ///////////////////////////////////////////////////////////////////////////////////////////////
741 // Implementation of nsURILoader
742 ///////////////////////////////////////////////////////////////////////////////////////////////
743
744 nsURILoader::nsURILoader()
745 {
746 #ifdef PR_LOGGING
747 if (!mLog) {
748 mLog = PR_NewLogModule("URILoader");
749 }
750 #endif
751 }
752
753 nsURILoader::~nsURILoader()
754 {
755 }
756
757 NS_IMPL_ADDREF(nsURILoader)
758 NS_IMPL_RELEASE(nsURILoader)
759
760 NS_INTERFACE_MAP_BEGIN(nsURILoader)
761 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader)
762 NS_INTERFACE_MAP_ENTRY(nsIURILoader)
763 NS_INTERFACE_MAP_END
764
765 NS_IMETHODIMP nsURILoader::RegisterContentListener(nsIURIContentListener * aContentListener)
766 {
767 nsresult rv = NS_OK;
768
769 nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
770 NS_ASSERTION(weakListener, "your URIContentListener must support weak refs!\n");
771
772 if (weakListener)
773 m_listeners.AppendObject(weakListener);
774
775 return rv;
776 }
777
778 NS_IMETHODIMP nsURILoader::UnRegisterContentListener(nsIURIContentListener * aContentListener)
779 {
780 nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
781 if (weakListener)
782 m_listeners.RemoveObject(weakListener);
783
784 return NS_OK;
785
786 }
787
788 NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
789 uint32_t aFlags,
790 nsIInterfaceRequestor *aWindowContext)
791 {
792 NS_ENSURE_ARG_POINTER(channel);
793
794 #ifdef PR_LOGGING
795 if (LOG_ENABLED()) {
796 nsCOMPtr<nsIURI> uri;
797 channel->GetURI(getter_AddRefs(uri));
798 nsAutoCString spec;
799 uri->GetAsciiSpec(spec);
800 LOG(("nsURILoader::OpenURI for %s", spec.get()));
801 }
802 #endif
803
804 nsCOMPtr<nsIStreamListener> loader;
805 nsresult rv = OpenChannel(channel,
806 aFlags,
807 aWindowContext,
808 false,
809 getter_AddRefs(loader));
810
811 if (NS_SUCCEEDED(rv)) {
812 // this method is not complete!!! Eventually, we should first go
813 // to the content listener and ask them for a protocol handler...
814 // if they don't give us one, we need to go to the registry and get
815 // the preferred protocol handler.
816
817 // But for now, I'm going to let necko do the work for us....
818 rv = channel->AsyncOpen(loader, nullptr);
819
820 // no content from this load - that's OK.
821 if (rv == NS_ERROR_NO_CONTENT) {
822 LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
823 rv = NS_OK;
824 }
825 } else if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {
826 // Not really an error, from this method's point of view
827 rv = NS_OK;
828 }
829 return rv;
830 }
831
832 nsresult nsURILoader::OpenChannel(nsIChannel* channel,
833 uint32_t aFlags,
834 nsIInterfaceRequestor* aWindowContext,
835 bool aChannelIsOpen,
836 nsIStreamListener** aListener)
837 {
838 NS_ASSERTION(channel, "Trying to open a null channel!");
839 NS_ASSERTION(aWindowContext, "Window context must not be null");
840
841 #ifdef PR_LOGGING
842 if (LOG_ENABLED()) {
843 nsCOMPtr<nsIURI> uri;
844 channel->GetURI(getter_AddRefs(uri));
845 nsAutoCString spec;
846 uri->GetAsciiSpec(spec);
847 LOG(("nsURILoader::OpenChannel for %s", spec.get()));
848 }
849 #endif
850
851 // Let the window context's uriListener know that the open is starting. This
852 // gives that window a chance to abort the load process.
853 nsCOMPtr<nsIURIContentListener> winContextListener(do_GetInterface(aWindowContext));
854 if (winContextListener) {
855 nsCOMPtr<nsIURI> uri;
856 channel->GetURI(getter_AddRefs(uri));
857 if (uri) {
858 bool doAbort = false;
859 winContextListener->OnStartURIOpen(uri, &doAbort);
860
861 if (doAbort) {
862 LOG((" OnStartURIOpen aborted load"));
863 return NS_ERROR_WONT_HANDLE_CONTENT;
864 }
865 }
866 }
867
868 // we need to create a DocumentOpenInfo object which will go ahead and open
869 // the url and discover the content type....
870 nsRefPtr<nsDocumentOpenInfo> loader =
871 new nsDocumentOpenInfo(aWindowContext, aFlags, this);
872
873 if (!loader) return NS_ERROR_OUT_OF_MEMORY;
874
875 // Set the correct loadgroup on the channel
876 nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext));
877
878 if (!loadGroup) {
879 // XXXbz This context is violating what we'd like to be the new uriloader
880 // api.... Set up a nsDocLoader to handle the loadgroup for this context.
881 // This really needs to go away!
882 nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext));
883 if (listener) {
884 nsCOMPtr<nsISupports> cookie;
885 listener->GetLoadCookie(getter_AddRefs(cookie));
886 if (!cookie) {
887 nsRefPtr<nsDocLoader> newDocLoader = new nsDocLoader();
888 if (!newDocLoader)
889 return NS_ERROR_OUT_OF_MEMORY;
890 nsresult rv = newDocLoader->Init();
891 if (NS_FAILED(rv))
892 return rv;
893 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader);
894 if (NS_FAILED(rv))
895 return rv;
896 cookie = nsDocLoader::GetAsSupports(newDocLoader);
897 listener->SetLoadCookie(cookie);
898 }
899 loadGroup = do_GetInterface(cookie);
900 }
901 }
902
903 // If the channel is pending, then we need to remove it from its current
904 // loadgroup
905 nsCOMPtr<nsILoadGroup> oldGroup;
906 channel->GetLoadGroup(getter_AddRefs(oldGroup));
907 if (aChannelIsOpen && !SameCOMIdentity(oldGroup, loadGroup)) {
908 // It is important to add the channel to the new group before
909 // removing it from the old one, so that the load isn't considered
910 // done as soon as the request is removed.
911 loadGroup->AddRequest(channel, nullptr);
912
913 if (oldGroup) {
914 oldGroup->RemoveRequest(channel, nullptr, NS_BINDING_RETARGETED);
915 }
916 }
917
918 channel->SetLoadGroup(loadGroup);
919
920 // prepare the loader for receiving data
921 nsresult rv = loader->Prepare();
922 if (NS_SUCCEEDED(rv))
923 NS_ADDREF(*aListener = loader);
924 return rv;
925 }
926
927 NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel,
928 uint32_t aFlags,
929 nsIInterfaceRequestor* aWindowContext,
930 nsIStreamListener** aListener)
931 {
932 bool pending;
933 if (NS_FAILED(channel->IsPending(&pending))) {
934 pending = false;
935 }
936
937 return OpenChannel(channel, aFlags, aWindowContext, pending, aListener);
938 }
939
940 NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie)
941 {
942 nsresult rv;
943 nsCOMPtr<nsIDocumentLoader> docLoader;
944
945 NS_ENSURE_ARG_POINTER(aLoadCookie);
946
947 docLoader = do_GetInterface(aLoadCookie, &rv);
948 if (docLoader) {
949 rv = docLoader->Stop();
950 }
951 return rv;
952 }
953

mercurial