LCOV - code coverage report
Current view: top level - uriloader/base - nsURILoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 162 358 45.3 %
Date: 2017-07-14 16:53:18 Functions: 18 25 72.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13