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.

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

mercurial