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