1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/base/nsURILoader.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,953 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sts=2 sw=2 et cin: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsURILoader.h" 1.11 +#include "nsAutoPtr.h" 1.12 +#include "nsIURIContentListener.h" 1.13 +#include "nsIContentHandler.h" 1.14 +#include "nsILoadGroup.h" 1.15 +#include "nsIDocumentLoader.h" 1.16 +#include "nsIWebProgress.h" 1.17 +#include "nsIWebProgressListener.h" 1.18 +#include "nsIIOService.h" 1.19 +#include "nsIServiceManager.h" 1.20 +#include "nsIStreamListener.h" 1.21 +#include "nsIURI.h" 1.22 +#include "nsIChannel.h" 1.23 +#include "nsIInterfaceRequestor.h" 1.24 +#include "nsIInterfaceRequestorUtils.h" 1.25 +#include "nsIProgressEventSink.h" 1.26 +#include "nsIInputStream.h" 1.27 +#include "nsIStreamConverterService.h" 1.28 +#include "nsWeakReference.h" 1.29 +#include "nsIHttpChannel.h" 1.30 +#include "nsIMultiPartChannel.h" 1.31 +#include "netCore.h" 1.32 +#include "nsCRT.h" 1.33 +#include "nsIDocShell.h" 1.34 +#include "nsIDocShellTreeItem.h" 1.35 +#include "nsIDocShellTreeOwner.h" 1.36 +#include "nsIThreadRetargetableStreamListener.h" 1.37 + 1.38 +#include "nsXPIDLString.h" 1.39 +#include "nsString.h" 1.40 +#include "nsNetUtil.h" 1.41 +#include "nsThreadUtils.h" 1.42 +#include "nsReadableUtils.h" 1.43 +#include "nsError.h" 1.44 + 1.45 +#include "nsICategoryManager.h" 1.46 +#include "nsCExternalHandlerService.h" // contains contractids for the helper app service 1.47 + 1.48 +#include "nsIMIMEHeaderParam.h" 1.49 +#include "nsNetCID.h" 1.50 + 1.51 +#include "nsMimeTypes.h" 1.52 + 1.53 +#include "nsDocLoader.h" 1.54 +#include "mozilla/Attributes.h" 1.55 +#include "mozilla/Preferences.h" 1.56 + 1.57 +#ifdef PR_LOGGING 1.58 +PRLogModuleInfo* nsURILoader::mLog = nullptr; 1.59 +#endif 1.60 + 1.61 +#define LOG(args) PR_LOG(nsURILoader::mLog, PR_LOG_DEBUG, args) 1.62 +#define LOG_ERROR(args) PR_LOG(nsURILoader::mLog, PR_LOG_ERROR, args) 1.63 +#define LOG_ENABLED() PR_LOG_TEST(nsURILoader::mLog, PR_LOG_DEBUG) 1.64 + 1.65 +#define NS_PREF_DISABLE_BACKGROUND_HANDLING \ 1.66 + "security.exthelperapp.disable_background_handling" 1.67 + 1.68 +/** 1.69 + * The nsDocumentOpenInfo contains the state required when a single 1.70 + * document is being opened in order to discover the content type... 1.71 + * Each instance remains alive until its target URL has been loaded 1.72 + * (or aborted). 1.73 + */ 1.74 +class nsDocumentOpenInfo MOZ_FINAL : public nsIStreamListener 1.75 + , public nsIThreadRetargetableStreamListener 1.76 +{ 1.77 +public: 1.78 + // Needed for nsCOMPtr to work right... Don't call this! 1.79 + nsDocumentOpenInfo(); 1.80 + 1.81 + // Real constructor 1.82 + // aFlags is a combination of the flags on nsIURILoader 1.83 + nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext, 1.84 + uint32_t aFlags, 1.85 + nsURILoader* aURILoader); 1.86 + 1.87 + NS_DECL_THREADSAFE_ISUPPORTS 1.88 + 1.89 + /** 1.90 + * Prepares this object for receiving data. The stream 1.91 + * listener methods of this class must not be called before calling this 1.92 + * method. 1.93 + */ 1.94 + nsresult Prepare(); 1.95 + 1.96 + // Call this (from OnStartRequest) to attempt to find an nsIStreamListener to 1.97 + // take the data off our hands. 1.98 + nsresult DispatchContent(nsIRequest *request, nsISupports * aCtxt); 1.99 + 1.100 + // Call this if we need to insert a stream converter from aSrcContentType to 1.101 + // aOutContentType into the StreamListener chain. DO NOT call it if the two 1.102 + // types are the same, since no conversion is needed in that case. 1.103 + nsresult ConvertData(nsIRequest *request, 1.104 + nsIURIContentListener *aListener, 1.105 + const nsACString & aSrcContentType, 1.106 + const nsACString & aOutContentType); 1.107 + 1.108 + /** 1.109 + * Function to attempt to use aListener to handle the load. If 1.110 + * true is returned, nothing else needs to be done; if false 1.111 + * is returned, then a different way of handling the load should be 1.112 + * tried. 1.113 + */ 1.114 + bool TryContentListener(nsIURIContentListener* aListener, 1.115 + nsIChannel* aChannel); 1.116 + 1.117 + // nsIRequestObserver methods: 1.118 + NS_DECL_NSIREQUESTOBSERVER 1.119 + 1.120 + // nsIStreamListener methods: 1.121 + NS_DECL_NSISTREAMLISTENER 1.122 + 1.123 + // nsIThreadRetargetableStreamListener 1.124 + NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER 1.125 +protected: 1.126 + ~nsDocumentOpenInfo(); 1.127 + 1.128 +protected: 1.129 + /** 1.130 + * The first content listener to try dispatching data to. Typically 1.131 + * the listener associated with the entity that originated the load. 1.132 + */ 1.133 + nsCOMPtr<nsIURIContentListener> m_contentListener; 1.134 + 1.135 + /** 1.136 + * The stream listener to forward nsIStreamListener notifications 1.137 + * to. This is set once the load is dispatched. 1.138 + */ 1.139 + nsCOMPtr<nsIStreamListener> m_targetStreamListener; 1.140 + 1.141 + /** 1.142 + * A pointer to the entity that originated the load. We depend on getting 1.143 + * things like nsIURIContentListeners, nsIDOMWindows, etc off of it. 1.144 + */ 1.145 + nsCOMPtr<nsIInterfaceRequestor> m_originalContext; 1.146 + 1.147 + /** 1.148 + * IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent 1.149 + * (also determines whether we use CanHandleContent or IsPreferred). 1.150 + * DONT_RETARGET means that we will only try m_originalContext, no other 1.151 + * listeners. 1.152 + */ 1.153 + uint32_t mFlags; 1.154 + 1.155 + /** 1.156 + * The type of the data we will be trying to dispatch. 1.157 + */ 1.158 + nsCString mContentType; 1.159 + 1.160 + /** 1.161 + * Reference to the URILoader service so we can access its list of 1.162 + * nsIURIContentListeners. 1.163 + */ 1.164 + nsRefPtr<nsURILoader> mURILoader; 1.165 +}; 1.166 + 1.167 +NS_IMPL_ADDREF(nsDocumentOpenInfo) 1.168 +NS_IMPL_RELEASE(nsDocumentOpenInfo) 1.169 + 1.170 +NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo) 1.171 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver) 1.172 + NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 1.173 + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 1.174 + NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener) 1.175 +NS_INTERFACE_MAP_END_THREADSAFE 1.176 + 1.177 +nsDocumentOpenInfo::nsDocumentOpenInfo() 1.178 +{ 1.179 + NS_NOTREACHED("This should never be called\n"); 1.180 +} 1.181 + 1.182 +nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext, 1.183 + uint32_t aFlags, 1.184 + nsURILoader* aURILoader) 1.185 + : m_originalContext(aWindowContext), 1.186 + mFlags(aFlags), 1.187 + mURILoader(aURILoader) 1.188 +{ 1.189 +} 1.190 + 1.191 +nsDocumentOpenInfo::~nsDocumentOpenInfo() 1.192 +{ 1.193 +} 1.194 + 1.195 +nsresult nsDocumentOpenInfo::Prepare() 1.196 +{ 1.197 + LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this)); 1.198 + 1.199 + nsresult rv; 1.200 + 1.201 + // ask our window context if it has a uri content listener... 1.202 + m_contentListener = do_GetInterface(m_originalContext, &rv); 1.203 + return rv; 1.204 +} 1.205 + 1.206 +NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest *request, nsISupports * aCtxt) 1.207 +{ 1.208 + LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this)); 1.209 + MOZ_ASSERT(request); 1.210 + if (!request) { 1.211 + return NS_ERROR_UNEXPECTED; 1.212 + } 1.213 + 1.214 + nsresult rv = NS_OK; 1.215 + 1.216 + // 1.217 + // Deal with "special" HTTP responses: 1.218 + // 1.219 + // - In the case of a 204 (No Content) or 205 (Reset Content) response, do 1.220 + // not try to find a content handler. Return NS_BINDING_ABORTED to cancel 1.221 + // the request. This has the effect of ensuring that the DocLoader does 1.222 + // not try to interpret this as a real request. 1.223 + // 1.224 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request, &rv)); 1.225 + 1.226 + if (NS_SUCCEEDED(rv)) { 1.227 + uint32_t responseCode = 0; 1.228 + 1.229 + rv = httpChannel->GetResponseStatus(&responseCode); 1.230 + 1.231 + if (NS_FAILED(rv)) { 1.232 + LOG_ERROR((" Failed to get HTTP response status")); 1.233 + 1.234 + // behave as in the canceled case 1.235 + return NS_OK; 1.236 + } 1.237 + 1.238 + LOG((" HTTP response status: %d", responseCode)); 1.239 + 1.240 + if (204 == responseCode || 205 == responseCode) { 1.241 + return NS_BINDING_ABORTED; 1.242 + } 1.243 + } 1.244 + 1.245 + // 1.246 + // Make sure that the transaction has succeeded, so far... 1.247 + // 1.248 + nsresult status; 1.249 + 1.250 + rv = request->GetStatus(&status); 1.251 + 1.252 + NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get request status!"); 1.253 + if (NS_FAILED(rv)) return rv; 1.254 + 1.255 + if (NS_FAILED(status)) { 1.256 + LOG_ERROR((" Request failed, status: 0x%08X", rv)); 1.257 + 1.258 + // 1.259 + // The transaction has already reported an error - so it will be torn 1.260 + // down. Therefore, it is not necessary to return an error code... 1.261 + // 1.262 + return NS_OK; 1.263 + } 1.264 + 1.265 + rv = DispatchContent(request, aCtxt); 1.266 + 1.267 + LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08X", m_targetStreamListener.get(), rv)); 1.268 + 1.269 + NS_ASSERTION(NS_SUCCEEDED(rv) || !m_targetStreamListener, 1.270 + "Must not have an m_targetStreamListener with a failure return!"); 1.271 + 1.272 + NS_ENSURE_SUCCESS(rv, rv); 1.273 + 1.274 + if (m_targetStreamListener) 1.275 + rv = m_targetStreamListener->OnStartRequest(request, aCtxt); 1.276 + 1.277 + LOG((" OnStartRequest returning: 0x%08X", rv)); 1.278 + 1.279 + return rv; 1.280 +} 1.281 + 1.282 +NS_IMETHODIMP 1.283 +nsDocumentOpenInfo::CheckListenerChain() 1.284 +{ 1.285 + NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!"); 1.286 + nsresult rv = NS_OK; 1.287 + nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = 1.288 + do_QueryInterface(m_targetStreamListener, &rv); 1.289 + if (retargetableListener) { 1.290 + rv = retargetableListener->CheckListenerChain(); 1.291 + } 1.292 + LOG(("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv %x", 1.293 + this, (NS_SUCCEEDED(rv) ? "success" : "failure"), 1.294 + (nsIStreamListener*)m_targetStreamListener, rv)); 1.295 + return rv; 1.296 +} 1.297 + 1.298 +NS_IMETHODIMP 1.299 +nsDocumentOpenInfo::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt, 1.300 + nsIInputStream * inStr, 1.301 + uint64_t sourceOffset, uint32_t count) 1.302 +{ 1.303 + // if we have retarged to the end stream listener, then forward the call.... 1.304 + // otherwise, don't do anything 1.305 + 1.306 + nsresult rv = NS_OK; 1.307 + 1.308 + if (m_targetStreamListener) 1.309 + rv = m_targetStreamListener->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count); 1.310 + return rv; 1.311 +} 1.312 + 1.313 +NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest *request, nsISupports *aCtxt, 1.314 + nsresult aStatus) 1.315 +{ 1.316 + LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this)); 1.317 + 1.318 + if ( m_targetStreamListener) 1.319 + { 1.320 + nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener); 1.321 + 1.322 + // If this is a multipart stream, we could get another 1.323 + // OnStartRequest after this... reset state. 1.324 + m_targetStreamListener = 0; 1.325 + mContentType.Truncate(); 1.326 + listener->OnStopRequest(request, aCtxt, aStatus); 1.327 + } 1.328 + 1.329 + // Remember... 1.330 + // In the case of multiplexed streams (such as multipart/x-mixed-replace) 1.331 + // these stream listener methods could be called again :-) 1.332 + // 1.333 + return NS_OK; 1.334 +} 1.335 + 1.336 +nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports * aCtxt) 1.337 +{ 1.338 + LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this, mContentType.get())); 1.339 + 1.340 + NS_PRECONDITION(!m_targetStreamListener, 1.341 + "Why do we already have a target stream listener?"); 1.342 + 1.343 + nsresult rv; 1.344 + nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request); 1.345 + if (!aChannel) { 1.346 + LOG_ERROR((" Request is not a channel. Bailing.")); 1.347 + return NS_ERROR_FAILURE; 1.348 + } 1.349 + 1.350 + NS_NAMED_LITERAL_CSTRING(anyType, "*/*"); 1.351 + if (mContentType.IsEmpty() || mContentType == anyType) { 1.352 + rv = aChannel->GetContentType(mContentType); 1.353 + if (NS_FAILED(rv)) return rv; 1.354 + LOG((" Got type from channel: '%s'", mContentType.get())); 1.355 + } 1.356 + 1.357 + bool isGuessFromExt = 1.358 + mContentType.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT); 1.359 + if (isGuessFromExt) { 1.360 + // Reset to application/octet-stream for now; no one other than the 1.361 + // external helper app service should see APPLICATION_GUESS_FROM_EXT. 1.362 + mContentType = APPLICATION_OCTET_STREAM; 1.363 + aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM)); 1.364 + } 1.365 + 1.366 + // Check whether the data should be forced to be handled externally. This 1.367 + // could happen because the Content-Disposition header is set so, or, in the 1.368 + // future, because the user has specified external handling for the MIME 1.369 + // type. 1.370 + bool forceExternalHandling = false; 1.371 + uint32_t disposition; 1.372 + rv = aChannel->GetContentDisposition(&disposition); 1.373 + if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT) 1.374 + forceExternalHandling = true; 1.375 + 1.376 + LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no")); 1.377 + 1.378 + // We're going to try to find a contentListener that can handle our data 1.379 + nsCOMPtr<nsIURIContentListener> contentListener; 1.380 + // The type or data the contentListener wants. 1.381 + nsXPIDLCString desiredContentType; 1.382 + 1.383 + if (!forceExternalHandling) 1.384 + { 1.385 + // 1.386 + // First step: See whether m_contentListener wants to handle this 1.387 + // content type. 1.388 + // 1.389 + if (m_contentListener && TryContentListener(m_contentListener, aChannel)) { 1.390 + LOG((" Success! Our default listener likes this type")); 1.391 + // All done here 1.392 + return NS_OK; 1.393 + } 1.394 + 1.395 + // If we aren't allowed to try other listeners, just skip through to 1.396 + // trying to convert the data. 1.397 + if (!(mFlags & nsIURILoader::DONT_RETARGET)) { 1.398 + 1.399 + // 1.400 + // Second step: See whether some other registered listener wants 1.401 + // to handle this content type. 1.402 + // 1.403 + int32_t count = mURILoader->m_listeners.Count(); 1.404 + nsCOMPtr<nsIURIContentListener> listener; 1.405 + for (int32_t i = 0; i < count; i++) { 1.406 + listener = do_QueryReferent(mURILoader->m_listeners[i]); 1.407 + if (listener) { 1.408 + if (TryContentListener(listener, aChannel)) { 1.409 + LOG((" Found listener registered on the URILoader")); 1.410 + return NS_OK; 1.411 + } 1.412 + } else { 1.413 + // remove from the listener list, reset i and update count 1.414 + mURILoader->m_listeners.RemoveObjectAt(i--); 1.415 + --count; 1.416 + } 1.417 + } 1.418 + 1.419 + // 1.420 + // Third step: Try to find a content listener that has not yet had 1.421 + // the chance to register, as it is contained in a not-yet-loaded 1.422 + // module, but which has registered a contract ID. 1.423 + // 1.424 + nsCOMPtr<nsICategoryManager> catman = 1.425 + do_GetService(NS_CATEGORYMANAGER_CONTRACTID); 1.426 + if (catman) { 1.427 + nsXPIDLCString contractidString; 1.428 + rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY, 1.429 + mContentType.get(), 1.430 + getter_Copies(contractidString)); 1.431 + if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) { 1.432 + LOG((" Listener contractid for '%s' is '%s'", 1.433 + mContentType.get(), contractidString.get())); 1.434 + 1.435 + listener = do_CreateInstance(contractidString); 1.436 + LOG((" Listener from category manager: 0x%p", listener.get())); 1.437 + 1.438 + if (listener && TryContentListener(listener, aChannel)) { 1.439 + LOG((" Listener from category manager likes this type")); 1.440 + return NS_OK; 1.441 + } 1.442 + } 1.443 + } 1.444 + 1.445 + // 1.446 + // Fourth step: try to find an nsIContentHandler for our type. 1.447 + // 1.448 + nsAutoCString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX); 1.449 + handlerContractID += mContentType; 1.450 + 1.451 + nsCOMPtr<nsIContentHandler> contentHandler = 1.452 + do_CreateInstance(handlerContractID.get()); 1.453 + if (contentHandler) { 1.454 + LOG((" Content handler found")); 1.455 + rv = contentHandler->HandleContent(mContentType.get(), 1.456 + m_originalContext, request); 1.457 + // XXXbz returning an error code to represent handling the 1.458 + // content is just bizarre! 1.459 + if (rv != NS_ERROR_WONT_HANDLE_CONTENT) { 1.460 + if (NS_FAILED(rv)) { 1.461 + // The content handler has unexpectedly failed. Cancel the request 1.462 + // just in case the handler didn't... 1.463 + LOG((" Content handler failed. Aborting load")); 1.464 + request->Cancel(rv); 1.465 + } 1.466 +#ifdef PR_LOGGING 1.467 + else { 1.468 + LOG((" Content handler taking over load")); 1.469 + } 1.470 +#endif 1.471 + 1.472 + return rv; 1.473 + } 1.474 + } 1.475 + } else { 1.476 + LOG((" DONT_RETARGET flag set, so skipped over random other content " 1.477 + "listeners and content handlers")); 1.478 + } 1.479 + 1.480 + // 1.481 + // Fifth step: If no listener prefers this type, see if any stream 1.482 + // converters exist to transform this content type into 1.483 + // some other. 1.484 + // 1.485 + // Don't do this if the server sent us a MIME type of "*/*" because they saw 1.486 + // it in our Accept header and got confused. 1.487 + // XXXbz have to be careful here; may end up in some sort of bizarre infinite 1.488 + // decoding loop. 1.489 + if (mContentType != anyType) { 1.490 + rv = ConvertData(request, m_contentListener, mContentType, anyType); 1.491 + if (NS_FAILED(rv)) { 1.492 + m_targetStreamListener = nullptr; 1.493 + } else if (m_targetStreamListener) { 1.494 + // We found a converter for this MIME type. We'll just pump data into it 1.495 + // and let the downstream nsDocumentOpenInfo handle things. 1.496 + LOG((" Converter taking over now")); 1.497 + return NS_OK; 1.498 + } 1.499 + } 1.500 + } 1.501 + 1.502 + NS_ASSERTION(!m_targetStreamListener, 1.503 + "If we found a listener, why are we not using it?"); 1.504 + 1.505 + if (mFlags & nsIURILoader::DONT_RETARGET) { 1.506 + LOG((" External handling forced or (listener not interested and no " 1.507 + "stream converter exists), and retargeting disallowed -> aborting")); 1.508 + return NS_ERROR_WONT_HANDLE_CONTENT; 1.509 + } 1.510 + 1.511 + // Before dispatching to the external helper app service, check for an HTTP 1.512 + // error page. If we got one, we don't want to handle it with a helper app, 1.513 + // really. 1.514 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request)); 1.515 + if (httpChannel) { 1.516 + bool requestSucceeded; 1.517 + httpChannel->GetRequestSucceeded(&requestSucceeded); 1.518 + if (!requestSucceeded) { 1.519 + // returning error from OnStartRequest will cancel the channel 1.520 + return NS_ERROR_FILE_NOT_FOUND; 1.521 + } 1.522 + } 1.523 + 1.524 + // Sixth step: 1.525 + // 1.526 + // All attempts to dispatch this content have failed. Just pass it off to 1.527 + // the helper app service. 1.528 + // 1.529 + 1.530 + // 1.531 + // Optionally, we may want to disable background handling by the external 1.532 + // helper application service. 1.533 + // 1.534 + if (mozilla::Preferences::GetBool(NS_PREF_DISABLE_BACKGROUND_HANDLING, 1.535 + false)) { 1.536 + // First, we will ensure that the parent docshell is in an active 1.537 + // state as we will disallow all external application handling unless it is 1.538 + // in the foreground. 1.539 + nsCOMPtr<nsIDocShell> docShell(do_GetInterface(m_originalContext)); 1.540 + if (!docShell) { 1.541 + // If we can't perform our security check we definitely don't want to go 1.542 + // any further! 1.543 + LOG(("Failed to get DocShell to ensure it is active before anding off to " 1.544 + "helper app service. Aborting.")); 1.545 + return NS_ERROR_FAILURE; 1.546 + } 1.547 + 1.548 + // Ensure the DocShell is active before continuing. 1.549 + bool isActive = false; 1.550 + docShell->GetIsActive(&isActive); 1.551 + if (!isActive) { 1.552 + LOG((" Check for active DocShell returned false. Aborting hand off to " 1.553 + "helper app service.")); 1.554 + return NS_ERROR_DOM_SECURITY_ERR; 1.555 + } 1.556 + } 1.557 + 1.558 + nsCOMPtr<nsIExternalHelperAppService> helperAppService = 1.559 + do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv); 1.560 + if (helperAppService) { 1.561 + LOG((" Passing load off to helper app service")); 1.562 + 1.563 + // Set these flags to indicate that the channel has been targeted and that 1.564 + // we are not using the original consumer. 1.565 + nsLoadFlags loadFlags = 0; 1.566 + request->GetLoadFlags(&loadFlags); 1.567 + request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI 1.568 + | nsIChannel::LOAD_TARGETED); 1.569 + 1.570 + if (isGuessFromExt) { 1.571 + mContentType = APPLICATION_GUESS_FROM_EXT; 1.572 + aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT)); 1.573 + } 1.574 + 1.575 + rv = helperAppService->DoContent(mContentType, 1.576 + request, 1.577 + m_originalContext, 1.578 + false, 1.579 + getter_AddRefs(m_targetStreamListener)); 1.580 + if (NS_FAILED(rv)) { 1.581 + request->SetLoadFlags(loadFlags); 1.582 + m_targetStreamListener = nullptr; 1.583 + } 1.584 + } 1.585 + 1.586 + NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv), 1.587 + "There is no way we should be successful at this point without a m_targetStreamListener"); 1.588 + return rv; 1.589 +} 1.590 + 1.591 +nsresult 1.592 +nsDocumentOpenInfo::ConvertData(nsIRequest *request, 1.593 + nsIURIContentListener* aListener, 1.594 + const nsACString& aSrcContentType, 1.595 + const nsACString& aOutContentType) 1.596 +{ 1.597 + LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this, 1.598 + PromiseFlatCString(aSrcContentType).get(), 1.599 + PromiseFlatCString(aOutContentType).get())); 1.600 + 1.601 + NS_PRECONDITION(aSrcContentType != aOutContentType, 1.602 + "ConvertData called when the two types are the same!"); 1.603 + nsresult rv = NS_OK; 1.604 + 1.605 + nsCOMPtr<nsIStreamConverterService> StreamConvService = 1.606 + do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); 1.607 + if (NS_FAILED(rv)) return rv; 1.608 + 1.609 + LOG((" Got converter service")); 1.610 + 1.611 + // When applying stream decoders, it is necessary to "insert" an 1.612 + // intermediate nsDocumentOpenInfo instance to handle the targeting of 1.613 + // the "final" stream or streams. 1.614 + // 1.615 + // For certain content types (ie. multi-part/x-mixed-replace) the input 1.616 + // stream is split up into multiple destination streams. This 1.617 + // intermediate instance is used to target these "decoded" streams... 1.618 + // 1.619 + nsRefPtr<nsDocumentOpenInfo> nextLink = 1.620 + new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader); 1.621 + if (!nextLink) return NS_ERROR_OUT_OF_MEMORY; 1.622 + 1.623 + LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get())); 1.624 + 1.625 + // Make sure nextLink starts with the contentListener that said it wanted the 1.626 + // results of this decode. 1.627 + nextLink->m_contentListener = aListener; 1.628 + // Also make sure it has to look for a stream listener to pump data into. 1.629 + nextLink->m_targetStreamListener = nullptr; 1.630 + 1.631 + // Make sure that nextLink treats the data as aOutContentType when 1.632 + // dispatching; that way even if the stream converters don't change the type 1.633 + // on the channel we will still do the right thing. If aOutContentType is 1.634 + // */*, that's OK -- that will just indicate to nextLink that it should get 1.635 + // the type off the channel. 1.636 + nextLink->mContentType = aOutContentType; 1.637 + 1.638 + // The following call sets m_targetStreamListener to the input end of the 1.639 + // stream converter and sets the output end of the stream converter to 1.640 + // nextLink. As we pump data into m_targetStreamListener the stream 1.641 + // converter will convert it and pass the converted data to nextLink. 1.642 + return StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(), 1.643 + PromiseFlatCString(aOutContentType).get(), 1.644 + nextLink, 1.645 + request, 1.646 + getter_AddRefs(m_targetStreamListener)); 1.647 +} 1.648 + 1.649 +bool 1.650 +nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener, 1.651 + nsIChannel* aChannel) 1.652 +{ 1.653 + LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x", 1.654 + this, mFlags)); 1.655 + 1.656 + NS_PRECONDITION(aListener, "Must have a non-null listener"); 1.657 + NS_PRECONDITION(aChannel, "Must have a channel"); 1.658 + 1.659 + bool listenerWantsContent = false; 1.660 + nsXPIDLCString typeToUse; 1.661 + 1.662 + if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) { 1.663 + aListener->IsPreferred(mContentType.get(), 1.664 + getter_Copies(typeToUse), 1.665 + &listenerWantsContent); 1.666 + } else { 1.667 + aListener->CanHandleContent(mContentType.get(), false, 1.668 + getter_Copies(typeToUse), 1.669 + &listenerWantsContent); 1.670 + } 1.671 + if (!listenerWantsContent) { 1.672 + LOG((" Listener is not interested")); 1.673 + return false; 1.674 + } 1.675 + 1.676 + if (!typeToUse.IsEmpty() && typeToUse != mContentType) { 1.677 + // Need to do a conversion here. 1.678 + 1.679 + nsresult rv = ConvertData(aChannel, aListener, mContentType, typeToUse); 1.680 + 1.681 + if (NS_FAILED(rv)) { 1.682 + // No conversion path -- we don't want this listener, if we got one 1.683 + m_targetStreamListener = nullptr; 1.684 + } 1.685 + 1.686 + LOG((" Found conversion: %s", m_targetStreamListener ? "yes" : "no")); 1.687 + 1.688 + // m_targetStreamListener is now the input end of the converter, and we can 1.689 + // just pump the data in there, if it exists. If it does not, we need to 1.690 + // try other nsIURIContentListeners. 1.691 + return m_targetStreamListener != nullptr; 1.692 + } 1.693 + 1.694 + // At this point, aListener wants data of type mContentType. Let 'em have 1.695 + // it. But first, if we are retargeting, set an appropriate flag on the 1.696 + // channel 1.697 + nsLoadFlags loadFlags = 0; 1.698 + aChannel->GetLoadFlags(&loadFlags); 1.699 + 1.700 + // Set this flag to indicate that the channel has been targeted at a final 1.701 + // consumer. This load flag is tested in nsDocLoader::OnProgress. 1.702 + nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED; 1.703 + 1.704 + nsCOMPtr<nsIURIContentListener> originalListener = 1.705 + do_GetInterface(m_originalContext); 1.706 + if (originalListener != aListener) { 1.707 + newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI; 1.708 + } 1.709 + aChannel->SetLoadFlags(loadFlags | newLoadFlags); 1.710 + 1.711 + bool abort = false; 1.712 + bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0; 1.713 + nsresult rv = aListener->DoContent(mContentType.get(), 1.714 + isPreferred, 1.715 + aChannel, 1.716 + getter_AddRefs(m_targetStreamListener), 1.717 + &abort); 1.718 + 1.719 + if (NS_FAILED(rv)) { 1.720 + LOG_ERROR((" DoContent failed")); 1.721 + 1.722 + // Unset the RETARGETED_DOCUMENT_URI flag if we set it... 1.723 + aChannel->SetLoadFlags(loadFlags); 1.724 + m_targetStreamListener = nullptr; 1.725 + return false; 1.726 + } 1.727 + 1.728 + if (abort) { 1.729 + // Nothing else to do here -- aListener is handling it all. Make 1.730 + // sure m_targetStreamListener is null so we don't do anything 1.731 + // after this point. 1.732 + LOG((" Listener has aborted the load")); 1.733 + m_targetStreamListener = nullptr; 1.734 + } 1.735 + 1.736 + NS_ASSERTION(abort || m_targetStreamListener, "DoContent returned no listener?"); 1.737 + 1.738 + // aListener is handling the load from this point on. 1.739 + return true; 1.740 +} 1.741 + 1.742 + 1.743 +/////////////////////////////////////////////////////////////////////////////////////////////// 1.744 +// Implementation of nsURILoader 1.745 +/////////////////////////////////////////////////////////////////////////////////////////////// 1.746 + 1.747 +nsURILoader::nsURILoader() 1.748 +{ 1.749 +#ifdef PR_LOGGING 1.750 + if (!mLog) { 1.751 + mLog = PR_NewLogModule("URILoader"); 1.752 + } 1.753 +#endif 1.754 +} 1.755 + 1.756 +nsURILoader::~nsURILoader() 1.757 +{ 1.758 +} 1.759 + 1.760 +NS_IMPL_ADDREF(nsURILoader) 1.761 +NS_IMPL_RELEASE(nsURILoader) 1.762 + 1.763 +NS_INTERFACE_MAP_BEGIN(nsURILoader) 1.764 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader) 1.765 + NS_INTERFACE_MAP_ENTRY(nsIURILoader) 1.766 +NS_INTERFACE_MAP_END 1.767 + 1.768 +NS_IMETHODIMP nsURILoader::RegisterContentListener(nsIURIContentListener * aContentListener) 1.769 +{ 1.770 + nsresult rv = NS_OK; 1.771 + 1.772 + nsWeakPtr weakListener = do_GetWeakReference(aContentListener); 1.773 + NS_ASSERTION(weakListener, "your URIContentListener must support weak refs!\n"); 1.774 + 1.775 + if (weakListener) 1.776 + m_listeners.AppendObject(weakListener); 1.777 + 1.778 + return rv; 1.779 +} 1.780 + 1.781 +NS_IMETHODIMP nsURILoader::UnRegisterContentListener(nsIURIContentListener * aContentListener) 1.782 +{ 1.783 + nsWeakPtr weakListener = do_GetWeakReference(aContentListener); 1.784 + if (weakListener) 1.785 + m_listeners.RemoveObject(weakListener); 1.786 + 1.787 + return NS_OK; 1.788 + 1.789 +} 1.790 + 1.791 +NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel, 1.792 + uint32_t aFlags, 1.793 + nsIInterfaceRequestor *aWindowContext) 1.794 +{ 1.795 + NS_ENSURE_ARG_POINTER(channel); 1.796 + 1.797 +#ifdef PR_LOGGING 1.798 + if (LOG_ENABLED()) { 1.799 + nsCOMPtr<nsIURI> uri; 1.800 + channel->GetURI(getter_AddRefs(uri)); 1.801 + nsAutoCString spec; 1.802 + uri->GetAsciiSpec(spec); 1.803 + LOG(("nsURILoader::OpenURI for %s", spec.get())); 1.804 + } 1.805 +#endif 1.806 + 1.807 + nsCOMPtr<nsIStreamListener> loader; 1.808 + nsresult rv = OpenChannel(channel, 1.809 + aFlags, 1.810 + aWindowContext, 1.811 + false, 1.812 + getter_AddRefs(loader)); 1.813 + 1.814 + if (NS_SUCCEEDED(rv)) { 1.815 + // this method is not complete!!! Eventually, we should first go 1.816 + // to the content listener and ask them for a protocol handler... 1.817 + // if they don't give us one, we need to go to the registry and get 1.818 + // the preferred protocol handler. 1.819 + 1.820 + // But for now, I'm going to let necko do the work for us.... 1.821 + rv = channel->AsyncOpen(loader, nullptr); 1.822 + 1.823 + // no content from this load - that's OK. 1.824 + if (rv == NS_ERROR_NO_CONTENT) { 1.825 + LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing")); 1.826 + rv = NS_OK; 1.827 + } 1.828 + } else if (rv == NS_ERROR_WONT_HANDLE_CONTENT) { 1.829 + // Not really an error, from this method's point of view 1.830 + rv = NS_OK; 1.831 + } 1.832 + return rv; 1.833 +} 1.834 + 1.835 +nsresult nsURILoader::OpenChannel(nsIChannel* channel, 1.836 + uint32_t aFlags, 1.837 + nsIInterfaceRequestor* aWindowContext, 1.838 + bool aChannelIsOpen, 1.839 + nsIStreamListener** aListener) 1.840 +{ 1.841 + NS_ASSERTION(channel, "Trying to open a null channel!"); 1.842 + NS_ASSERTION(aWindowContext, "Window context must not be null"); 1.843 + 1.844 +#ifdef PR_LOGGING 1.845 + if (LOG_ENABLED()) { 1.846 + nsCOMPtr<nsIURI> uri; 1.847 + channel->GetURI(getter_AddRefs(uri)); 1.848 + nsAutoCString spec; 1.849 + uri->GetAsciiSpec(spec); 1.850 + LOG(("nsURILoader::OpenChannel for %s", spec.get())); 1.851 + } 1.852 +#endif 1.853 + 1.854 + // Let the window context's uriListener know that the open is starting. This 1.855 + // gives that window a chance to abort the load process. 1.856 + nsCOMPtr<nsIURIContentListener> winContextListener(do_GetInterface(aWindowContext)); 1.857 + if (winContextListener) { 1.858 + nsCOMPtr<nsIURI> uri; 1.859 + channel->GetURI(getter_AddRefs(uri)); 1.860 + if (uri) { 1.861 + bool doAbort = false; 1.862 + winContextListener->OnStartURIOpen(uri, &doAbort); 1.863 + 1.864 + if (doAbort) { 1.865 + LOG((" OnStartURIOpen aborted load")); 1.866 + return NS_ERROR_WONT_HANDLE_CONTENT; 1.867 + } 1.868 + } 1.869 + } 1.870 + 1.871 + // we need to create a DocumentOpenInfo object which will go ahead and open 1.872 + // the url and discover the content type.... 1.873 + nsRefPtr<nsDocumentOpenInfo> loader = 1.874 + new nsDocumentOpenInfo(aWindowContext, aFlags, this); 1.875 + 1.876 + if (!loader) return NS_ERROR_OUT_OF_MEMORY; 1.877 + 1.878 + // Set the correct loadgroup on the channel 1.879 + nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext)); 1.880 + 1.881 + if (!loadGroup) { 1.882 + // XXXbz This context is violating what we'd like to be the new uriloader 1.883 + // api.... Set up a nsDocLoader to handle the loadgroup for this context. 1.884 + // This really needs to go away! 1.885 + nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext)); 1.886 + if (listener) { 1.887 + nsCOMPtr<nsISupports> cookie; 1.888 + listener->GetLoadCookie(getter_AddRefs(cookie)); 1.889 + if (!cookie) { 1.890 + nsRefPtr<nsDocLoader> newDocLoader = new nsDocLoader(); 1.891 + if (!newDocLoader) 1.892 + return NS_ERROR_OUT_OF_MEMORY; 1.893 + nsresult rv = newDocLoader->Init(); 1.894 + if (NS_FAILED(rv)) 1.895 + return rv; 1.896 + rv = nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader); 1.897 + if (NS_FAILED(rv)) 1.898 + return rv; 1.899 + cookie = nsDocLoader::GetAsSupports(newDocLoader); 1.900 + listener->SetLoadCookie(cookie); 1.901 + } 1.902 + loadGroup = do_GetInterface(cookie); 1.903 + } 1.904 + } 1.905 + 1.906 + // If the channel is pending, then we need to remove it from its current 1.907 + // loadgroup 1.908 + nsCOMPtr<nsILoadGroup> oldGroup; 1.909 + channel->GetLoadGroup(getter_AddRefs(oldGroup)); 1.910 + if (aChannelIsOpen && !SameCOMIdentity(oldGroup, loadGroup)) { 1.911 + // It is important to add the channel to the new group before 1.912 + // removing it from the old one, so that the load isn't considered 1.913 + // done as soon as the request is removed. 1.914 + loadGroup->AddRequest(channel, nullptr); 1.915 + 1.916 + if (oldGroup) { 1.917 + oldGroup->RemoveRequest(channel, nullptr, NS_BINDING_RETARGETED); 1.918 + } 1.919 + } 1.920 + 1.921 + channel->SetLoadGroup(loadGroup); 1.922 + 1.923 + // prepare the loader for receiving data 1.924 + nsresult rv = loader->Prepare(); 1.925 + if (NS_SUCCEEDED(rv)) 1.926 + NS_ADDREF(*aListener = loader); 1.927 + return rv; 1.928 +} 1.929 + 1.930 +NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel, 1.931 + uint32_t aFlags, 1.932 + nsIInterfaceRequestor* aWindowContext, 1.933 + nsIStreamListener** aListener) 1.934 +{ 1.935 + bool pending; 1.936 + if (NS_FAILED(channel->IsPending(&pending))) { 1.937 + pending = false; 1.938 + } 1.939 + 1.940 + return OpenChannel(channel, aFlags, aWindowContext, pending, aListener); 1.941 +} 1.942 + 1.943 +NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie) 1.944 +{ 1.945 + nsresult rv; 1.946 + nsCOMPtr<nsIDocumentLoader> docLoader; 1.947 + 1.948 + NS_ENSURE_ARG_POINTER(aLoadCookie); 1.949 + 1.950 + docLoader = do_GetInterface(aLoadCookie, &rv); 1.951 + if (docLoader) { 1.952 + rv = docLoader->Stop(); 1.953 + } 1.954 + return rv; 1.955 +} 1.956 +