uriloader/base/nsURILoader.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial