LCOV - code coverage report
Current view: directory - uriloader/base - nsURILoader.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 316 145 45.9 %
Date: 2012-06-02 Functions: 24 19 79.2 %

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

Generated by: LCOV version 1.7