LCOV - code coverage report
Current view: top level - uriloader/base - nsDocLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 436 542 80.4 %
Date: 2017-07-14 16:53:18 Functions: 50 60 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nspr.h"
       7             : #include "mozilla/Logging.h"
       8             : #include "mozilla/IntegerPrintfMacros.h"
       9             : 
      10             : #include "nsDocLoader.h"
      11             : #include "nsCURILoader.h"
      12             : #include "nsNetUtil.h"
      13             : #include "nsIHttpChannel.h"
      14             : #include "nsIWebProgressListener2.h"
      15             : 
      16             : #include "nsIServiceManager.h"
      17             : #include "nsXPIDLString.h"
      18             : 
      19             : #include "nsIURL.h"
      20             : #include "nsCOMPtr.h"
      21             : #include "nscore.h"
      22             : #include "nsWeakPtr.h"
      23             : #include "nsAutoPtr.h"
      24             : #include "nsQueryObject.h"
      25             : 
      26             : #include "nsIDOMWindow.h"
      27             : 
      28             : #include "nsIStringBundle.h"
      29             : #include "nsIScriptSecurityManager.h"
      30             : 
      31             : #include "nsITransport.h"
      32             : #include "nsISocketTransport.h"
      33             : #include "nsIDocShell.h"
      34             : #include "nsIDOMDocument.h"
      35             : #include "nsIDocument.h"
      36             : #include "nsPresContext.h"
      37             : #include "nsIAsyncVerifyRedirectCallback.h"
      38             : 
      39             : using mozilla::DebugOnly;
      40             : using mozilla::LogLevel;
      41             : 
      42             : static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
      43             : 
      44             : //
      45             : // Log module for nsIDocumentLoader logging...
      46             : //
      47             : // To enable logging (see mozilla/Logging.h for full details):
      48             : //
      49             : //    set MOZ_LOG=DocLoader:5
      50             : //    set MOZ_LOG_FILE=debug.log
      51             : //
      52             : // this enables LogLevel::Debug level information and places all output in
      53             : // the file 'debug.log'.
      54             : //
      55             : mozilla::LazyLogModule gDocLoaderLog("DocLoader");
      56             : 
      57             : 
      58             : #if defined(DEBUG)
      59         721 : void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
      60             : {
      61         721 :     if (request)
      62         721 :         request->GetName(name);
      63             :     else
      64           0 :         name.AssignLiteral("???");
      65         721 : }
      66             : #endif /* DEBUG */
      67             : 
      68             : 
      69             : 
      70             : void
      71          76 : nsDocLoader::RequestInfoHashInitEntry(PLDHashEntryHdr* entry,
      72             :                                       const void* key)
      73             : {
      74             :   // Initialize the entry with placement new
      75          76 :   new (entry) nsRequestInfo(key);
      76          76 : }
      77             : 
      78             : void
      79          76 : nsDocLoader::RequestInfoHashClearEntry(PLDHashTable* table,
      80             :                                        PLDHashEntryHdr* entry)
      81             : {
      82          76 :   nsRequestInfo* info = static_cast<nsRequestInfo *>(entry);
      83          76 :   info->~nsRequestInfo();
      84          76 : }
      85             : 
      86             : // this is used for mListenerInfoList.Contains()
      87             : template <>
      88             : class nsDefaultComparator <nsDocLoader::nsListenerInfo, nsIWebProgressListener*> {
      89             :   public:
      90          32 :     bool Equals(const nsDocLoader::nsListenerInfo& aInfo,
      91             :                 nsIWebProgressListener* const& aListener) const {
      92             :       nsCOMPtr<nsIWebProgressListener> listener =
      93          64 :                                        do_QueryReferent(aInfo.mWeakListener);
      94          64 :       return aListener == listener;
      95             :     }
      96             : };
      97             : 
      98             : /* static */ const PLDHashTableOps nsDocLoader::sRequestInfoHashOps =
      99             : {
     100             :   PLDHashTable::HashVoidPtrKeyStub,
     101             :   PLDHashTable::MatchEntryStub,
     102             :   PLDHashTable::MoveEntryStub,
     103             :   nsDocLoader::RequestInfoHashClearEntry,
     104             :   nsDocLoader::RequestInfoHashInitEntry
     105             : };
     106             : 
     107           8 : nsDocLoader::nsDocLoader()
     108             :   : mParent(nullptr),
     109             :     mProgressStateFlags(0),
     110             :     mCurrentSelfProgress(0),
     111             :     mMaxSelfProgress(0),
     112             :     mCurrentTotalProgress(0),
     113             :     mMaxTotalProgress(0),
     114             :     mRequestInfoHash(&sRequestInfoHashOps, sizeof(nsRequestInfo)),
     115             :     mCompletedTotalProgress(0),
     116             :     mIsLoadingDocument(false),
     117             :     mIsRestoringDocument(false),
     118             :     mDontFlushLayout(false),
     119           8 :     mIsFlushingLayout(false)
     120             : {
     121           8 :   ClearInternalProgress();
     122             : 
     123           8 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     124             :          ("DocLoader:%p: created.\n", this));
     125           8 : }
     126             : 
     127             : nsresult
     128          12 : nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
     129             : {
     130          12 :   mParent = aParent;
     131          12 :   return NS_OK;
     132             : }
     133             : 
     134             : nsresult
     135           8 : nsDocLoader::Init()
     136             : {
     137           8 :   nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
     138           8 :   if (NS_FAILED(rv)) return rv;
     139             : 
     140           8 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     141             :          ("DocLoader:%p: load group %p.\n", this, mLoadGroup.get()));
     142             : 
     143           8 :   return NS_OK;
     144             : }
     145             : 
     146           0 : nsDocLoader::~nsDocLoader()
     147             : {
     148             :                 /*
     149             :                         |ClearWeakReferences()| here is intended to prevent people holding weak references
     150             :                         from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
     151             :                         subsequent |Release()| will try to destroy me.  At this point there should be only
     152             :                         weak references remaining (otherwise, we wouldn't be getting destroyed).
     153             : 
     154             :                         An alternative would be incrementing our refcount (consider it a compressed flag
     155             :                         saying "Don't re-destroy.").  I haven't yet decided which is better. [scc]
     156             :                 */
     157             :   // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
     158             :   // this needed?
     159           0 :   ClearWeakReferences();
     160             : 
     161           0 :   Destroy();
     162             : 
     163           0 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     164             :          ("DocLoader:%p: deleted.\n", this));
     165           0 : }
     166             : 
     167             : 
     168             : /*
     169             :  * Implementation of ISupports methods...
     170             :  */
     171        7215 : NS_IMPL_ADDREF(nsDocLoader)
     172        7139 : NS_IMPL_RELEASE(nsDocLoader)
     173             : 
     174        3092 : NS_INTERFACE_MAP_BEGIN(nsDocLoader)
     175        3092 :    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
     176        2898 :    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
     177        2594 :    NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
     178        2518 :    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     179        2515 :    NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
     180        2470 :    NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
     181        2358 :    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
     182         985 :    NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
     183         985 :    NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
     184         981 :    NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     185         981 :    if (aIID.Equals(kThisImplCID))
     186           9 :      foundInterface = static_cast<nsIDocumentLoader *>(this);
     187             :    else
     188         972 : NS_INTERFACE_MAP_END
     189             : 
     190             : 
     191             : /*
     192             :  * Implementation of nsIInterfaceRequestor methods...
     193             :  */
     194         751 : NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
     195             : {
     196         751 :   nsresult rv = NS_ERROR_NO_INTERFACE;
     197             : 
     198         751 :   NS_ENSURE_ARG_POINTER(aSink);
     199             : 
     200         751 :   if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
     201           6 :     *aSink = mLoadGroup;
     202           6 :     NS_IF_ADDREF((nsISupports*)*aSink);
     203           6 :     rv = NS_OK;
     204             :   } else {
     205         745 :     rv = QueryInterface(aIID, aSink);
     206             :   }
     207             : 
     208         751 :   return rv;
     209             : }
     210             : 
     211             : /* static */
     212             : already_AddRefed<nsDocLoader>
     213           9 : nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
     214             : {
     215          18 :   RefPtr<nsDocLoader> ret = do_QueryObject(aSupports);
     216          18 :   return ret.forget();
     217             : }
     218             : 
     219             : /* static */
     220             : nsresult
     221           6 : nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
     222             : {
     223             :   nsresult rv;
     224             :   nsCOMPtr<nsIDocumentLoader> docLoaderService =
     225          12 :     do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
     226           6 :   NS_ENSURE_SUCCESS(rv, rv);
     227             : 
     228          12 :   RefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
     229           6 :   NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
     230             : 
     231           6 :   return rootDocLoader->AddChildLoader(aDocLoader);
     232             : }
     233             : 
     234             : NS_IMETHODIMP
     235           8 : nsDocLoader::Stop(void)
     236             : {
     237           8 :   nsresult rv = NS_OK;
     238             : 
     239           8 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     240             :          ("DocLoader:%p: Stop() called\n", this));
     241             : 
     242           8 :   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader, Stop, ());
     243             : 
     244           8 :   if (mLoadGroup)
     245           8 :     rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
     246             : 
     247             :   // Don't report that we're flushing layout so IsBusy returns false after a
     248             :   // Stop call.
     249           8 :   mIsFlushingLayout = false;
     250             : 
     251             :   // Clear out mChildrenInOnload.  We want to make sure to fire our
     252             :   // onload at this point, and there's no issue with mChildrenInOnload
     253             :   // after this, since mDocumentRequest will be null after the
     254             :   // DocLoaderIsEmpty() call.
     255           8 :   mChildrenInOnload.Clear();
     256             : 
     257             :   // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
     258             :   // etc, as needed.  We could be getting into here from a subframe onload, in
     259             :   // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
     260             :   // happened yet, Canceling the loadgroup did nothing (because it was already
     261             :   // empty), and we're about to start a new load (which is what triggered this
     262             :   // Stop() call).
     263             : 
     264             :   // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
     265             :   // we wouldn't need the call here....
     266             : 
     267           8 :   NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
     268           8 :   DocLoaderIsEmpty(false);
     269             : 
     270           8 :   return rv;
     271             : }
     272             : 
     273             : 
     274             : bool
     275          80 : nsDocLoader::IsBusy()
     276             : {
     277             :   nsresult rv;
     278             : 
     279             :   //
     280             :   // A document loader is busy if either:
     281             :   //
     282             :   //   1. One of its children is in the middle of an onload handler.  Note that
     283             :   //      the handler may have already removed this child from mChildList!
     284             :   //   2. It is currently loading a document and either has parts of it still
     285             :   //      loading, or has a busy child docloader.
     286             :   //   3. It's currently flushing layout in DocLoaderIsEmpty().
     287             :   //
     288             : 
     289          80 :   if (mChildrenInOnload.Count() || mIsFlushingLayout) {
     290           0 :     return true;
     291             :   }
     292             : 
     293             :   /* Is this document loader busy? */
     294          80 :   if (!mIsLoadingDocument) {
     295          10 :     return false;
     296             :   }
     297             : 
     298             :   bool busy;
     299          70 :   rv = mLoadGroup->IsPending(&busy);
     300          70 :   if (NS_FAILED(rv)) {
     301           0 :     return false;
     302             :   }
     303          70 :   if (busy) {
     304          58 :     return true;
     305             :   }
     306             : 
     307             :   /* check its child document loaders... */
     308          12 :   uint32_t count = mChildList.Length();
     309          14 :   for (uint32_t i=0; i < count; i++) {
     310           2 :     nsIDocumentLoader* loader = ChildAt(i);
     311             : 
     312             :     // This is a safe cast, because we only put nsDocLoader objects into the
     313             :     // array
     314           2 :     if (loader && static_cast<nsDocLoader*>(loader)->IsBusy())
     315           0 :       return true;
     316             :   }
     317             : 
     318          12 :   return false;
     319             : }
     320             : 
     321             : NS_IMETHODIMP
     322           0 : nsDocLoader::GetContainer(nsISupports** aResult)
     323             : {
     324           0 :    NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
     325             : 
     326           0 :    return NS_OK;
     327             : }
     328             : 
     329             : NS_IMETHODIMP
     330           0 : nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
     331             : {
     332           0 :   nsresult rv = NS_OK;
     333             : 
     334           0 :   if (nullptr == aResult) {
     335           0 :     rv = NS_ERROR_NULL_POINTER;
     336             :   } else {
     337           0 :     *aResult = mLoadGroup;
     338           0 :     NS_IF_ADDREF(*aResult);
     339             :   }
     340           0 :   return rv;
     341             : }
     342             : 
     343             : void
     344           1 : nsDocLoader::Destroy()
     345             : {
     346           1 :   Stop();
     347             : 
     348             :   // Remove the document loader from the parent list of loaders...
     349           1 :   if (mParent)
     350             :   {
     351           2 :     DebugOnly<nsresult> rv = mParent->RemoveChildLoader(this);
     352           1 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveChildLoader failed");
     353             :   }
     354             : 
     355             :   // Release all the information about network requests...
     356           1 :   ClearRequestInfoHash();
     357             : 
     358           1 :   mListenerInfoList.Clear();
     359           1 :   mListenerInfoList.Compact();
     360             : 
     361           1 :   mDocumentRequest = nullptr;
     362             : 
     363           1 :   if (mLoadGroup)
     364           1 :     mLoadGroup->SetGroupObserver(nullptr);
     365             : 
     366           1 :   DestroyChildren();
     367           1 : }
     368             : 
     369             : void
     370           9 : nsDocLoader::DestroyChildren()
     371             : {
     372           9 :   uint32_t count = mChildList.Length();
     373             :   // if the doc loader still has children...we need to enumerate the
     374             :   // children and make them null out their back ptr to the parent doc
     375             :   // loader
     376           9 :   for (uint32_t i=0; i < count; i++)
     377             :   {
     378           0 :     nsIDocumentLoader* loader = ChildAt(i);
     379             : 
     380           0 :     if (loader) {
     381             :       // This is a safe cast, as we only put nsDocLoader objects into the
     382             :       // array
     383             :       DebugOnly<nsresult> rv =
     384           0 :         static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nullptr);
     385           0 :       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetDocLoaderParent failed");
     386             :     }
     387             :   }
     388           9 :   mChildList.Clear();
     389           9 : }
     390             : 
     391             : NS_IMETHODIMP
     392          76 : nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
     393             : {
     394             :   // called each time a request is added to the group.
     395             : 
     396          76 :   if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
     397           0 :     nsAutoCString name;
     398           0 :     request->GetName(name);
     399             : 
     400           0 :     uint32_t count = 0;
     401           0 :     if (mLoadGroup)
     402           0 :       mLoadGroup->GetActiveCount(&count);
     403             : 
     404           0 :     MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     405             :            ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
     406             :             this, request, name.get(),
     407             :             (mIsLoadingDocument ? "true" : "false"),
     408             :             count));
     409             :   }
     410             : 
     411          76 :   bool bJustStartedLoading = false;
     412             : 
     413          76 :   nsLoadFlags loadFlags = 0;
     414          76 :   request->GetLoadFlags(&loadFlags);
     415             : 
     416          76 :   if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
     417           6 :       bJustStartedLoading = true;
     418           6 :       mIsLoadingDocument = true;
     419           6 :       ClearInternalProgress(); // only clear our progress if we are starting a new load....
     420             :   }
     421             : 
     422             :   //
     423             :   // Create a new nsRequestInfo for the request that is starting to
     424             :   // load...
     425             :   //
     426          76 :   AddRequestInfo(request);
     427             : 
     428             :   //
     429             :   // Only fire a doStartDocumentLoad(...) if the document loader
     430             :   // has initiated a load...  Otherwise, this notification has
     431             :   // resulted from a request being added to the load group.
     432             :   //
     433          76 :   if (mIsLoadingDocument) {
     434          62 :     if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
     435             :       //
     436             :       // Make sure that the document channel is null at this point...
     437             :       // (unless its been redirected)
     438             :       //
     439           6 :       NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
     440             :                    !(mDocumentRequest.get()),
     441             :                    "Overwriting an existing document channel!");
     442             : 
     443             :       // This request is associated with the entire document...
     444           6 :       mDocumentRequest = request;
     445           6 :       mLoadGroup->SetDefaultLoadRequest(request);
     446             : 
     447             :       // Only fire the start document load notification for the first
     448             :       // document URI...  Do not fire it again for redirections
     449             :       //
     450           6 :       if (bJustStartedLoading) {
     451             :         // Update the progress status state
     452           6 :         mProgressStateFlags = nsIWebProgressListener::STATE_START;
     453             : 
     454             :         // Fire the start document load notification
     455           6 :         doStartDocumentLoad();
     456           6 :         return NS_OK;
     457             :       }
     458             :     }
     459             :   }
     460             : 
     461          70 :   NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
     462             :                "mDocumentRequest MUST be set for the duration of a page load!");
     463             : 
     464             :   // This is the only way to catch document request start event after a redirect
     465             :   // has occured without changing inherited Firefox behaviour significantly.
     466             :   // Problem description:
     467             :   // The combination of |STATE_START + STATE_IS_DOCUMENT| is only sent for
     468             :   // initial request (see |doStartDocumentLoad| call above).
     469             :   // And |STATE_REDIRECTING + STATE_IS_DOCUMENT| is sent with old channel, which
     470             :   // makes it impossible to filter by destination URL (see
     471             :   // |AsyncOnChannelRedirect| implementation).
     472             :   // Fixing any of those bugs may cause unpredictable consequences in any part
     473             :   // of the browser, so we just add a custom flag for this exact situation.
     474          70 :   int32_t extraFlags = 0;
     475         126 :   if (mIsLoadingDocument &&
     476         112 :       !bJustStartedLoading &&
     477          56 :       (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) &&
     478           0 :       (loadFlags & nsIChannel::LOAD_REPLACE)) {
     479           0 :     extraFlags = nsIWebProgressListener::STATE_IS_REDIRECTED_DOCUMENT;
     480             :   }
     481          70 :   doStartURLLoad(request, extraFlags);
     482             : 
     483          70 :   return NS_OK;
     484             : }
     485             : 
     486             : NS_IMETHODIMP
     487          76 : nsDocLoader::OnStopRequest(nsIRequest *aRequest,
     488             :                            nsISupports *aCtxt,
     489             :                            nsresult aStatus)
     490             : {
     491          76 :   nsresult rv = NS_OK;
     492             : 
     493          76 :   if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
     494           0 :     nsAutoCString name;
     495           0 :     aRequest->GetName(name);
     496             : 
     497           0 :     uint32_t count = 0;
     498           0 :     if (mLoadGroup)
     499           0 :       mLoadGroup->GetActiveCount(&count);
     500             : 
     501           0 :     MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     502             :            ("DocLoader:%p: OnStopRequest[%p](%s) status=%" PRIx32 " mIsLoadingDocument=%s, %u active URLs",
     503             :            this, aRequest, name.get(),
     504             :             static_cast<uint32_t>(aStatus), (mIsLoadingDocument ? "true" : "false"),
     505             :            count));
     506             :   }
     507             : 
     508          76 :   bool bFireTransferring = false;
     509             : 
     510             :   //
     511             :   // Set the Maximum progress to the same value as the current progress.
     512             :   // Since the URI has finished loading, all the data is there.  Also,
     513             :   // this will allow a more accurate estimation of the max progress (in case
     514             :   // the old value was unknown ie. -1)
     515             :   //
     516          76 :   nsRequestInfo *info = GetRequestInfo(aRequest);
     517          76 :   if (info) {
     518             :     // Null out mLastStatus now so we don't find it when looking for
     519             :     // status from now on.  This destroys the nsStatusInfo and hence
     520             :     // removes it from our list.
     521          76 :     info->mLastStatus = nullptr;
     522             : 
     523          76 :     int64_t oldMax = info->mMaxProgress;
     524             : 
     525          76 :     info->mMaxProgress = info->mCurrentProgress;
     526             : 
     527             :     //
     528             :     // If a request whose content-length was previously unknown has just
     529             :     // finished loading, then use this new data to try to calculate a
     530             :     // mMaxSelfProgress...
     531             :     //
     532          76 :     if ((oldMax < int64_t(0)) && (mMaxSelfProgress < int64_t(0))) {
     533           1 :       mMaxSelfProgress = CalculateMaxProgress();
     534             :     }
     535             : 
     536             :     // As we know the total progress of this request now, save it to be part
     537             :     // of CalculateMaxProgress() result. We need to remove the info from the
     538             :     // hash, see bug 480713.
     539          76 :     mCompletedTotalProgress += info->mMaxProgress;
     540             : 
     541             :     //
     542             :     // Determine whether a STATE_TRANSFERRING notification should be
     543             :     // 'synthesized'.
     544             :     //
     545             :     // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
     546             :     // nsRequestInfo::mCurrentProgress are both 0, then the
     547             :     // STATE_TRANSFERRING notification has not been fired yet...
     548             :     //
     549          76 :     if ((oldMax == 0) && (info->mCurrentProgress == 0)) {
     550          42 :       nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
     551             : 
     552             :       // Only fire a TRANSFERRING notification if the request is also a
     553             :       // channel -- data transfer requires a nsIChannel!
     554             :       //
     555          21 :       if (channel) {
     556           4 :         if (NS_SUCCEEDED(aStatus)) {
     557           1 :           bFireTransferring = true;
     558             :         }
     559             :         //
     560             :         // If the request failed (for any reason other than being
     561             :         // redirected or retargeted), the TRANSFERRING notification can
     562             :         // still be fired if a HTTP connection was established to a server.
     563             :         //
     564           3 :         else if (aStatus != NS_BINDING_REDIRECTED &&
     565             :                  aStatus != NS_BINDING_RETARGETED) {
     566             :           //
     567             :           // Only if the load has been targeted (see bug 268483)...
     568             :           //
     569             :           uint32_t lf;
     570           3 :           channel->GetLoadFlags(&lf);
     571           3 :           if (lf & nsIChannel::LOAD_TARGETED) {
     572           2 :             nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
     573           1 :             if (httpChannel) {
     574             :               uint32_t responseCode;
     575           0 :               rv = httpChannel->GetResponseStatus(&responseCode);
     576           0 :               if (NS_SUCCEEDED(rv)) {
     577             :                 //
     578             :                 // A valid server status indicates that a connection was
     579             :                 // established to the server... So, fire the notification
     580             :                 // even though a failure occurred later...
     581             :                 //
     582           0 :                 bFireTransferring = true;
     583             :               }
     584             :             }
     585             :           }
     586             :         }
     587             :       }
     588             :     }
     589             :   }
     590             : 
     591          76 :   if (bFireTransferring) {
     592             :     // Send a STATE_TRANSFERRING notification for the request.
     593             :     int32_t flags;
     594             : 
     595           1 :     flags = nsIWebProgressListener::STATE_TRANSFERRING |
     596             :             nsIWebProgressListener::STATE_IS_REQUEST;
     597             :     //
     598             :     // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
     599             :     //
     600           1 :     if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
     601           1 :       mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
     602             : 
     603             :       // Send STATE_TRANSFERRING for the document too...
     604           1 :       flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
     605             :     }
     606             : 
     607           1 :     FireOnStateChange(this, aRequest, flags, NS_OK);
     608             :   }
     609             : 
     610             :   //
     611             :   // Fire the OnStateChange(...) notification for stop request
     612             :   //
     613          76 :   doStopURLLoad(aRequest, aStatus);
     614             : 
     615             :   // Clear this request out of the hash to avoid bypass of FireOnStateChange
     616             :   // when address of the request is reused.
     617          76 :   RemoveRequestInfo(aRequest);
     618             : 
     619             :   //
     620             :   // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
     621             :   // load.  This will handle removing the request from our hashtable as needed.
     622             :   //
     623          76 :   if (mIsLoadingDocument) {
     624         124 :     nsCOMPtr<nsIDocShell> ds = do_QueryInterface(static_cast<nsIRequestObserver*>(this));
     625          62 :     bool doNotFlushLayout = false;
     626          62 :     if (ds) {
     627             :       // Don't do unexpected layout flushes while we're in process of restoring
     628             :       // a document from the bfcache.
     629          62 :       ds->GetRestoringDocument(&doNotFlushLayout);
     630             :     }
     631          62 :     DocLoaderIsEmpty(!doNotFlushLayout);
     632             :   }
     633             : 
     634          76 :   return NS_OK;
     635             : }
     636             : 
     637             : 
     638           4 : nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
     639             : {
     640           4 :   nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
     641           4 :   if (NS_SUCCEEDED(rv)) {
     642           4 :     rv = aChild->SetDocLoaderParent(nullptr);
     643             :   }
     644           4 :   return rv;
     645             : }
     646             : 
     647           8 : nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
     648             : {
     649           8 :   nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     650           8 :   if (NS_SUCCEEDED(rv)) {
     651           8 :     rv = aChild->SetDocLoaderParent(this);
     652             :   }
     653           8 :   return rv;
     654             : }
     655             : 
     656           1 : NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
     657             : {
     658           1 :   if (!mDocumentRequest) {
     659           1 :     *aChannel = nullptr;
     660           1 :     return NS_OK;
     661             :   }
     662             : 
     663           0 :   return CallQueryInterface(mDocumentRequest, aChannel);
     664             : }
     665             : 
     666             : 
     667          76 : void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
     668             : {
     669          76 :   if (mIsLoadingDocument) {
     670             :     /* In the unimagineably rude circumstance that onload event handlers
     671             :        triggered by this function actually kill the window ... ok, it's
     672             :        not unimagineable; it's happened ... this deathgrip keeps this object
     673             :        alive long enough to survive this function call. */
     674          70 :     nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
     675             : 
     676             :     // Don't flush layout if we're still busy.
     677          64 :     if (IsBusy()) {
     678          58 :       return;
     679             :     }
     680             : 
     681           6 :     NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
     682           6 :     NS_ASSERTION(mDocumentRequest, "No Document Request!");
     683             : 
     684             :     // The load group for this DocumentLoader is idle.  Flush if we need to.
     685           6 :     if (aFlushLayout && !mDontFlushLayout) {
     686          12 :       nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(GetAsSupports(this));
     687          12 :       nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
     688           6 :       if (doc) {
     689             :         // We start loads from style resolution, so we need to flush out style
     690             :         // no matter what.  If we have user fonts, we also need to flush layout,
     691             :         // since the reflow is what starts font loads.
     692           6 :         mozilla::FlushType flushType = mozilla::FlushType::Style;
     693           6 :         nsIPresShell* shell = doc->GetShell();
     694           6 :         if (shell) {
     695             :           // Be safe in case this presshell is in teardown now
     696           4 :           nsPresContext* presContext = shell->GetPresContext();
     697           4 :           if (presContext && presContext->GetUserFontSet()) {
     698           0 :             flushType = mozilla::FlushType::Layout;
     699             :           }
     700             :         }
     701           6 :         mDontFlushLayout = mIsFlushingLayout = true;
     702           6 :         doc->FlushPendingNotifications(flushType);
     703           6 :         mDontFlushLayout = mIsFlushingLayout = false;
     704             :       }
     705             :     }
     706             : 
     707             :     // And now check whether we're really busy; that might have changed with
     708             :     // the layout flush.
     709             :     // Note, mDocumentRequest can be null if the flushing above re-entered this
     710             :     // method.
     711           6 :     if (!IsBusy() && mDocumentRequest) {
     712             :       // Clear out our request info hash, now that our load really is done and
     713             :       // we don't need it anymore to CalculateMaxProgress().
     714           6 :       ClearInternalProgress();
     715             : 
     716           6 :       MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     717             :              ("DocLoader:%p: Is now idle...\n", this));
     718             : 
     719          12 :       nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
     720             : 
     721           6 :       mDocumentRequest = nullptr;
     722           6 :       mIsLoadingDocument = false;
     723             : 
     724             :       // Update the progress status state - the document is done
     725           6 :       mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
     726             : 
     727             : 
     728           6 :       nsresult loadGroupStatus = NS_OK;
     729           6 :       mLoadGroup->GetStatus(&loadGroupStatus);
     730             : 
     731             :       //
     732             :       // New code to break the circular reference between
     733             :       // the load group and the docloader...
     734             :       //
     735           6 :       mLoadGroup->SetDefaultLoadRequest(nullptr);
     736             : 
     737             :       // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
     738             :       // it even if our onload handler removes us from the docloader tree.
     739          12 :       RefPtr<nsDocLoader> parent = mParent;
     740             : 
     741             :       // Note that if calling ChildEnteringOnload() on the parent returns false
     742             :       // then calling our onload handler is not safe.  That can only happen on
     743             :       // OOM, so that's ok.
     744           6 :       if (!parent || parent->ChildEnteringOnload(this)) {
     745             :         // Do nothing with our state after firing the
     746             :         // OnEndDocumentLoad(...). The document loader may be loading a *new*
     747             :         // document - if LoadDocument() was called from a handler!
     748             :         //
     749           6 :         doStopDocumentLoad(docRequest, loadGroupStatus);
     750             : 
     751           6 :         if (parent) {
     752           6 :           parent->ChildDoneWithOnload(this);
     753             :         }
     754             :       }
     755             :     }
     756             :   }
     757             : }
     758             : 
     759           6 : void nsDocLoader::doStartDocumentLoad(void)
     760             : {
     761             : 
     762             : #if defined(DEBUG)
     763          12 :   nsAutoCString buffer;
     764             : 
     765           6 :   GetURIStringFromRequest(mDocumentRequest, buffer);
     766           6 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     767             :          ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
     768             :           "\tURI: %s \n",
     769             :           this, buffer.get()));
     770             : #endif /* DEBUG */
     771             : 
     772             :   // Fire an OnStatus(...) notification STATE_START.  This indicates
     773             :   // that the document represented by mDocumentRequest has started to
     774             :   // load...
     775           6 :   FireOnStateChange(this,
     776             :                     mDocumentRequest,
     777             :                     nsIWebProgressListener::STATE_START |
     778             :                     nsIWebProgressListener::STATE_IS_DOCUMENT |
     779             :                     nsIWebProgressListener::STATE_IS_REQUEST |
     780             :                     nsIWebProgressListener::STATE_IS_WINDOW |
     781             :                     nsIWebProgressListener::STATE_IS_NETWORK,
     782           6 :                     NS_OK);
     783           6 : }
     784             : 
     785          70 : void nsDocLoader::doStartURLLoad(nsIRequest *request, int32_t aExtraFlags)
     786             : {
     787             : #if defined(DEBUG)
     788         140 :   nsAutoCString buffer;
     789             : 
     790          70 :   GetURIStringFromRequest(request, buffer);
     791          70 :     MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     792             :           ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
     793             :            "\tURI: %s\n",
     794             :             this, buffer.get()));
     795             : #endif /* DEBUG */
     796             : 
     797          70 :   FireOnStateChange(this,
     798             :                     request,
     799             :                     nsIWebProgressListener::STATE_START |
     800             :                     nsIWebProgressListener::STATE_IS_REQUEST |
     801             :                     aExtraFlags,
     802          70 :                     NS_OK);
     803          70 : }
     804             : 
     805          76 : void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
     806             : {
     807             : #if defined(DEBUG)
     808         152 :   nsAutoCString buffer;
     809             : 
     810          76 :   GetURIStringFromRequest(request, buffer);
     811          76 :     MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     812             :           ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
     813             :            "\tURI: %s status=%" PRIx32 "\n",
     814             :            this, buffer.get(), static_cast<uint32_t>(aStatus)));
     815             : #endif /* DEBUG */
     816             : 
     817          76 :   FireOnStateChange(this,
     818             :                     request,
     819             :                     nsIWebProgressListener::STATE_STOP |
     820             :                     nsIWebProgressListener::STATE_IS_REQUEST,
     821          76 :                     aStatus);
     822             : 
     823             :   // Fire a status change message for the most recent unfinished
     824             :   // request to make sure that the displayed status is not outdated.
     825          76 :   if (!mStatusInfoList.isEmpty()) {
     826           1 :     nsStatusInfo* statusInfo = mStatusInfoList.getFirst();
     827           1 :     FireOnStatusChange(this, statusInfo->mRequest,
     828             :                        statusInfo->mStatusCode,
     829           1 :                        statusInfo->mStatusMessage.get());
     830             :   }
     831          76 : }
     832             : 
     833           6 : void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
     834             :                                          nsresult aStatus)
     835             : {
     836             : #if defined(DEBUG)
     837          12 :   nsAutoCString buffer;
     838             : 
     839           6 :   GetURIStringFromRequest(request, buffer);
     840           6 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
     841             :          ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
     842             :          "\tURI: %s Status=%" PRIx32 "\n",
     843             :           this, buffer.get(), static_cast<uint32_t>(aStatus)));
     844             : #endif /* DEBUG */
     845             : 
     846             :   // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
     847             :   // Grab our parent chain before doing that so we can still dispatch
     848             :   // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
     849             :   // the onload handlers rearrange the docshell tree.
     850          12 :   WebProgressList list;
     851           6 :   GatherAncestorWebProgresses(list);
     852             : 
     853             :   //
     854             :   // Fire an OnStateChange(...) notification indicating the the
     855             :   // current document has finished loading...
     856             :   //
     857             :   int32_t flags = nsIWebProgressListener::STATE_STOP |
     858           6 :                   nsIWebProgressListener::STATE_IS_DOCUMENT;
     859          20 :   for (uint32_t i = 0; i < list.Length(); ++i) {
     860          14 :     list[i]->DoFireOnStateChange(this, request, flags, aStatus);
     861             :   }
     862             : 
     863             :   //
     864             :   // Fire a final OnStateChange(...) notification indicating the the
     865             :   // current document has finished loading...
     866             :   //
     867           6 :   flags = nsIWebProgressListener::STATE_STOP |
     868             :           nsIWebProgressListener::STATE_IS_WINDOW |
     869             :           nsIWebProgressListener::STATE_IS_NETWORK;
     870          20 :   for (uint32_t i = 0; i < list.Length(); ++i) {
     871          14 :     list[i]->DoFireOnStateChange(this, request, flags, aStatus);
     872             :   }
     873           6 : }
     874             : 
     875             : ////////////////////////////////////////////////////////////////////////////////////
     876             : // The following section contains support for nsIWebProgress and related stuff
     877             : ////////////////////////////////////////////////////////////////////////////////////
     878             : 
     879             : NS_IMETHODIMP
     880          21 : nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
     881             :                                  uint32_t aNotifyMask)
     882             : {
     883          21 :   if (mListenerInfoList.Contains(aListener)) {
     884             :     // The listener is already registered!
     885           1 :     return NS_ERROR_FAILURE;
     886             :   }
     887             : 
     888          40 :   nsWeakPtr listener = do_GetWeakReference(aListener);
     889          20 :   if (!listener) {
     890           0 :     return NS_ERROR_INVALID_ARG;
     891             :   }
     892             : 
     893          40 :   return mListenerInfoList.AppendElement(nsListenerInfo(listener, aNotifyMask)) ?
     894          20 :          NS_OK : NS_ERROR_OUT_OF_MEMORY;
     895             : }
     896             : 
     897             : NS_IMETHODIMP
     898           1 : nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
     899             : {
     900           1 :   return mListenerInfoList.RemoveElement(aListener) ? NS_OK : NS_ERROR_FAILURE;
     901             : }
     902             : 
     903             : NS_IMETHODIMP
     904          87 : nsDocLoader::GetDOMWindow(mozIDOMWindowProxy **aResult)
     905             : {
     906          87 :   return CallGetInterface(this, aResult);
     907             : }
     908             : 
     909             : NS_IMETHODIMP
     910           6 : nsDocLoader::GetDOMWindowID(uint64_t *aResult)
     911             : {
     912           6 :   *aResult = 0;
     913             : 
     914          12 :   nsCOMPtr<mozIDOMWindowProxy> window;
     915           6 :   nsresult rv = GetDOMWindow(getter_AddRefs(window));
     916           6 :   NS_ENSURE_SUCCESS(rv, rv);
     917             : 
     918          12 :   nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window);
     919           6 :   NS_ENSURE_STATE(piwindow);
     920             : 
     921           6 :   MOZ_ASSERT(piwindow->IsOuterWindow());
     922           6 :   *aResult = piwindow->WindowID();
     923           6 :   return NS_OK;
     924             : }
     925             : 
     926             : NS_IMETHODIMP
     927           6 : nsDocLoader::GetInnerDOMWindowID(uint64_t *aResult)
     928             : {
     929           6 :   *aResult = 0;
     930             : 
     931          12 :   nsCOMPtr<mozIDOMWindowProxy> window;
     932           6 :   nsresult rv = GetDOMWindow(getter_AddRefs(window));
     933           6 :   NS_ENSURE_SUCCESS(rv, rv);
     934             : 
     935          12 :   nsCOMPtr<nsPIDOMWindowOuter> outer = nsPIDOMWindowOuter::From(window);
     936           6 :   NS_ENSURE_STATE(outer);
     937             : 
     938           6 :   nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
     939           6 :   if (!inner) {
     940             :     // If we don't have an inner window, return 0.
     941           0 :     return NS_OK;
     942             :   }
     943             : 
     944           6 :   MOZ_ASSERT(inner->IsInnerWindow());
     945           6 :   *aResult = inner->WindowID();
     946           6 :   return NS_OK;
     947             : }
     948             : 
     949             : NS_IMETHODIMP
     950          18 : nsDocLoader::GetIsTopLevel(bool *aResult)
     951             : {
     952          18 :   *aResult = false;
     953             : 
     954          36 :   nsCOMPtr<mozIDOMWindowProxy> window;
     955          18 :   GetDOMWindow(getter_AddRefs(window));
     956          18 :   if (window) {
     957          36 :     nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window);
     958          18 :     NS_ENSURE_STATE(piwindow);
     959             : 
     960          36 :     nsCOMPtr<nsPIDOMWindowOuter> topWindow = piwindow->GetTop();
     961          18 :     *aResult = piwindow == topWindow;
     962             :   }
     963             : 
     964          18 :   return NS_OK;
     965             : }
     966             : 
     967             : NS_IMETHODIMP
     968           8 : nsDocLoader::GetIsLoadingDocument(bool *aIsLoadingDocument)
     969             : {
     970           8 :   *aIsLoadingDocument = mIsLoadingDocument;
     971             : 
     972           8 :   return NS_OK;
     973             : }
     974             : 
     975             : NS_IMETHODIMP
     976           0 : nsDocLoader::GetLoadType(uint32_t *aLoadType)
     977             : {
     978           0 :   *aLoadType = 0;
     979             : 
     980           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     981             : }
     982             : 
     983          86 : int64_t nsDocLoader::GetMaxTotalProgress()
     984             : {
     985          86 :   int64_t newMaxTotal = 0;
     986             : 
     987          86 :   uint32_t count = mChildList.Length();
     988         126 :   for (uint32_t i=0; i < count; i++)
     989             :   {
     990          40 :     int64_t individualProgress = 0;
     991          40 :     nsIDocumentLoader* docloader = ChildAt(i);
     992          40 :     if (docloader)
     993             :     {
     994             :       // Cast is safe since all children are nsDocLoader too
     995          40 :       individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
     996             :     }
     997          40 :     if (individualProgress < int64_t(0)) // if one of the elements doesn't know it's size
     998             :                                          // then none of them do
     999             :     {
    1000           0 :        newMaxTotal = int64_t(-1);
    1001           0 :        break;
    1002             :     }
    1003             :     else
    1004          40 :      newMaxTotal += individualProgress;
    1005             :   }
    1006             : 
    1007          86 :   int64_t progress = -1;
    1008          86 :   if (mMaxSelfProgress >= int64_t(0) && newMaxTotal >= int64_t(0))
    1009          86 :     progress = newMaxTotal + mMaxSelfProgress;
    1010             : 
    1011          86 :   return progress;
    1012             : }
    1013             : 
    1014             : ////////////////////////////////////////////////////////////////////////////////////
    1015             : // The following section contains support for nsIProgressEventSink which is used to
    1016             : // pass progress and status between the actual request and the doc loader. The doc loader
    1017             : // then turns around and makes the right web progress calls based on this information.
    1018             : ////////////////////////////////////////////////////////////////////////////////////
    1019             : 
    1020          55 : NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt,
    1021             :                                       int64_t aProgress, int64_t aProgressMax)
    1022             : {
    1023          55 :   int64_t progressDelta = 0;
    1024             : 
    1025             :   //
    1026             :   // Update the RequestInfo entry with the new progress data
    1027             :   //
    1028          55 :   if (nsRequestInfo* info = GetRequestInfo(aRequest)) {
    1029             :     // Update info->mCurrentProgress before we call FireOnStateChange,
    1030             :     // since that can make the "info" pointer invalid.
    1031          55 :     int64_t oldCurrentProgress = info->mCurrentProgress;
    1032          55 :     progressDelta = aProgress - oldCurrentProgress;
    1033          55 :     info->mCurrentProgress = aProgress;
    1034             : 
    1035             :     // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
    1036          55 :     if (!info->mUploading && (int64_t(0) == oldCurrentProgress) && (int64_t(0) == info->mMaxProgress)) {
    1037             :       //
    1038             :       // If we receive an OnProgress event from a toplevel channel that the URI Loader
    1039             :       // has not yet targeted, then we must suppress the event.  This is necessary to
    1040             :       // ensure that webprogresslisteners do not get confused when the channel is
    1041             :       // finally targeted.  See bug 257308.
    1042             :       //
    1043          55 :       nsLoadFlags lf = 0;
    1044          55 :       aRequest->GetLoadFlags(&lf);
    1045          55 :       if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
    1046           0 :         MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
    1047             :             ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
    1048           0 :         return NS_OK;
    1049             :       }
    1050             : 
    1051             :       //
    1052             :       // This is the first progress notification for the entry.  If
    1053             :       // (aMaxProgress != -1) then the content-length of the data is known,
    1054             :       // so update mMaxSelfProgress...  Otherwise, set it to -1 to indicate
    1055             :       // that the content-length is no longer known.
    1056             :       //
    1057          55 :       if (aProgressMax != -1) {
    1058          54 :         mMaxSelfProgress  += aProgressMax;
    1059          54 :         info->mMaxProgress = aProgressMax;
    1060             :       } else {
    1061           1 :         mMaxSelfProgress   =  int64_t(-1);
    1062           1 :         info->mMaxProgress =  int64_t(-1);
    1063             :       }
    1064             : 
    1065             :       // Send a STATE_TRANSFERRING notification for the request.
    1066             :       int32_t flags;
    1067             : 
    1068          55 :       flags = nsIWebProgressListener::STATE_TRANSFERRING |
    1069             :               nsIWebProgressListener::STATE_IS_REQUEST;
    1070             :       //
    1071             :       // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
    1072             :       //
    1073          55 :       if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
    1074           3 :         mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
    1075             : 
    1076             :         // Send STATE_TRANSFERRING for the document too...
    1077           3 :         flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
    1078             :       }
    1079             : 
    1080          55 :       FireOnStateChange(this, aRequest, flags, NS_OK);
    1081             :     }
    1082             : 
    1083             :     // Update our overall current progress count.
    1084          55 :     mCurrentSelfProgress += progressDelta;
    1085             :   }
    1086             :   //
    1087             :   // The request is not part of the load group, so ignore its progress
    1088             :   // information...
    1089             :   //
    1090             :   else {
    1091             : #if defined(DEBUG)
    1092           0 :     nsAutoCString buffer;
    1093             : 
    1094           0 :     GetURIStringFromRequest(aRequest, buffer);
    1095           0 :     MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
    1096             :            ("DocLoader:%p OOPS - No Request Info for: %s\n",
    1097             :             this, buffer.get()));
    1098             : #endif /* DEBUG */
    1099             : 
    1100           0 :     return NS_OK;
    1101             :   }
    1102             : 
    1103             :   //
    1104             :   // Fire progress notifications out to any registered nsIWebProgressListeners
    1105             :   //
    1106          55 :   FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
    1107          55 :                        mCurrentTotalProgress, mMaxTotalProgress);
    1108             : 
    1109          55 :   return NS_OK;
    1110             : }
    1111             : 
    1112           9 : NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt,
    1113             :                                         nsresult aStatus, const char16_t* aStatusArg)
    1114             : {
    1115             :   //
    1116             :   // Fire progress notifications out to any registered nsIWebProgressListeners
    1117             :   //
    1118           9 :   if (aStatus != NS_OK) {
    1119             :     // Remember the current status for this request
    1120             :     nsRequestInfo *info;
    1121           9 :     info = GetRequestInfo(aRequest);
    1122           9 :     if (info) {
    1123           9 :       bool uploading = (aStatus == NS_NET_STATUS_WRITING ||
    1124           9 :                         aStatus == NS_NET_STATUS_SENDING_TO);
    1125             :       // If switching from uploading to downloading (or vice versa), then we
    1126             :       // need to reset our progress counts.  This is designed with HTTP form
    1127             :       // submission in mind, where an upload is performed followed by download
    1128             :       // of possibly several documents.
    1129           9 :       if (info->mUploading != uploading) {
    1130           0 :         mCurrentSelfProgress  = mMaxSelfProgress  = 0;
    1131           0 :         mCurrentTotalProgress = mMaxTotalProgress = 0;
    1132           0 :         mCompletedTotalProgress = 0;
    1133           0 :         info->mUploading = uploading;
    1134           0 :         info->mCurrentProgress = 0;
    1135           0 :         info->mMaxProgress = 0;
    1136             :       }
    1137             :     }
    1138             : 
    1139             :     nsCOMPtr<nsIStringBundleService> sbs =
    1140          18 :       mozilla::services::GetStringBundleService();
    1141           9 :     if (!sbs)
    1142           0 :       return NS_ERROR_FAILURE;
    1143          18 :     nsXPIDLString msg;
    1144          18 :     nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg,
    1145          18 :                                            getter_Copies(msg));
    1146           9 :     if (NS_FAILED(rv))
    1147           0 :       return rv;
    1148             : 
    1149             :     // Keep around the message. In case a request finishes, we need to make sure
    1150             :     // to send the status message of another request to our user to that we
    1151             :     // don't display, for example, "Transferring" messages for requests that are
    1152             :     // already done.
    1153           9 :     if (info) {
    1154           9 :       if (!info->mLastStatus) {
    1155           4 :         info->mLastStatus = new nsStatusInfo(aRequest);
    1156             :       } else {
    1157             :         // We're going to move it to the front of the list, so remove
    1158             :         // it from wherever it is now.
    1159           5 :         info->mLastStatus->remove();
    1160             :       }
    1161           9 :       info->mLastStatus->mStatusMessage = msg;
    1162           9 :       info->mLastStatus->mStatusCode = aStatus;
    1163             :       // Put the info at the front of the list
    1164           9 :       mStatusInfoList.insertFront(info->mLastStatus);
    1165             :     }
    1166           9 :     FireOnStatusChange(this, aRequest, aStatus, msg);
    1167             :   }
    1168           9 :   return NS_OK;
    1169             : }
    1170             : 
    1171          20 : void nsDocLoader::ClearInternalProgress()
    1172             : {
    1173          20 :   ClearRequestInfoHash();
    1174             : 
    1175          20 :   mCurrentSelfProgress  = mMaxSelfProgress  = 0;
    1176          20 :   mCurrentTotalProgress = mMaxTotalProgress = 0;
    1177          20 :   mCompletedTotalProgress = 0;
    1178             : 
    1179          20 :   mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
    1180          20 : }
    1181             : 
    1182             : /**
    1183             :  * |_code| is executed for every listener matching |_flag|
    1184             :  * |listener| should be used inside |_code| as the nsIWebProgressListener var.
    1185             :  */
    1186             : #define NOTIFY_LISTENERS(_flag, _code)                     \
    1187             : PR_BEGIN_MACRO                                             \
    1188             :   nsCOMPtr<nsIWebProgressListener> listener;               \
    1189             :   ListenerArray::BackwardIterator iter(mListenerInfoList); \
    1190             :   while (iter.HasMore()) {                                 \
    1191             :     nsListenerInfo &info = iter.GetNext();                 \
    1192             :     if (!(info.mNotifyMask & (_flag))) {                   \
    1193             :       continue;                                            \
    1194             :     }                                                      \
    1195             :     listener = do_QueryReferent(info.mWeakListener);       \
    1196             :     if (!listener) {                                       \
    1197             :       iter.Remove();                                       \
    1198             :       continue;                                            \
    1199             :     }                                                      \
    1200             :     _code                                                  \
    1201             :   }                                                        \
    1202             :   mListenerInfoList.Compact();                             \
    1203             : PR_END_MACRO
    1204             : 
    1205         110 : void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
    1206             :                                        nsIRequest *request,
    1207             :                                        int64_t aProgress,
    1208             :                                        int64_t aProgressMax,
    1209             :                                        int64_t aProgressDelta,
    1210             :                                        int64_t aTotalProgress,
    1211             :                                        int64_t aMaxTotalProgress)
    1212             : {
    1213         110 :   if (mIsLoadingDocument) {
    1214          46 :     mCurrentTotalProgress += aProgressDelta;
    1215          46 :     mMaxTotalProgress = GetMaxTotalProgress();
    1216             : 
    1217          46 :     aTotalProgress    = mCurrentTotalProgress;
    1218          46 :     aMaxTotalProgress = mMaxTotalProgress;
    1219             :   }
    1220             : 
    1221             : #if defined(DEBUG)
    1222         220 :   nsAutoCString buffer;
    1223             : 
    1224         110 :   GetURIStringFromRequest(request, buffer);
    1225         110 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
    1226             :          ("DocLoader:%p: Progress (%s): curSelf: %" PRId64 " maxSelf: %"
    1227             :           PRId64 " curTotal: %" PRId64 " maxTotal %" PRId64 "\n",
    1228             :           this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
    1229             : #endif /* DEBUG */
    1230             : 
    1231         110 :   NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_PROGRESS,
    1232             :     // XXX truncates 64-bit to 32-bit
    1233             :     listener->OnProgressChange(aLoadInitiator,request,
    1234             :                                int32_t(aProgress), int32_t(aProgressMax),
    1235             :                                int32_t(aTotalProgress), int32_t(aMaxTotalProgress));
    1236             :   );
    1237             : 
    1238             :   // Pass the notification up to the parent...
    1239         110 :   if (mParent) {
    1240          55 :     mParent->FireOnProgressChange(aLoadInitiator, request,
    1241             :                                   aProgress, aProgressMax,
    1242             :                                   aProgressDelta,
    1243          55 :                                   aTotalProgress, aMaxTotalProgress);
    1244             :   }
    1245         110 : }
    1246             : 
    1247         214 : void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList)
    1248             : {
    1249         653 :   for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
    1250         439 :     aList.AppendElement(loader);
    1251             :   }
    1252         214 : }
    1253             : 
    1254         208 : void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
    1255             :                                     nsIRequest *aRequest,
    1256             :                                     int32_t aStateFlags,
    1257             :                                     nsresult aStatus)
    1258             : {
    1259         416 :   WebProgressList list;
    1260         208 :   GatherAncestorWebProgresses(list);
    1261         633 :   for (uint32_t i = 0; i < list.Length(); ++i) {
    1262         425 :     list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
    1263             :   }
    1264         208 : }
    1265             : 
    1266         453 : void nsDocLoader::DoFireOnStateChange(nsIWebProgress * const aProgress,
    1267             :                                       nsIRequest * const aRequest,
    1268             :                                       int32_t &aStateFlags,
    1269             :                                       const nsresult aStatus)
    1270             : {
    1271             :   //
    1272             :   // Remove the STATE_IS_NETWORK bit if necessary.
    1273             :   //
    1274             :   // The rule is to remove this bit, if the notification has been passed
    1275             :   // up from a child WebProgress, and the current WebProgress is already
    1276             :   // active...
    1277             :   //
    1278         637 :   if (mIsLoadingDocument &&
    1279         194 :       (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) &&
    1280          10 :       (this != aProgress)) {
    1281           4 :     aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
    1282             :   }
    1283             : 
    1284             :   // Add the STATE_RESTORING bit if necessary.
    1285         453 :   if (mIsRestoringDocument)
    1286           0 :     aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
    1287             : 
    1288             : #if defined(DEBUG)
    1289         906 :   nsAutoCString buffer;
    1290             : 
    1291         453 :   GetURIStringFromRequest(aRequest, buffer);
    1292         453 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
    1293             :          ("DocLoader:%p: Status (%s): code: %x\n",
    1294             :          this, buffer.get(), aStateFlags));
    1295             : #endif /* DEBUG */
    1296             : 
    1297         453 :   NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
    1298             : 
    1299         453 :   NOTIFY_LISTENERS(((aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL),
    1300             :     listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
    1301             :   );
    1302         453 : }
    1303             : 
    1304             : 
    1305             : 
    1306             : void
    1307          15 : nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
    1308             :                                   nsIRequest* aRequest,
    1309             :                                   nsIURI *aUri,
    1310             :                                   uint32_t aFlags)
    1311             : {
    1312          15 :   NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_LOCATION,
    1313             :     MOZ_LOG(gDocLoaderLog, LogLevel::Debug, ("DocLoader [%p] calling %p->OnLocationChange", this, listener.get()));
    1314             :     listener->OnLocationChange(aWebProgress, aRequest, aUri, aFlags);
    1315             :   );
    1316             : 
    1317             :   // Pass the notification up to the parent...
    1318          15 :   if (mParent) {
    1319           8 :     mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
    1320             :   }
    1321          15 : }
    1322             : 
    1323             : void
    1324          20 : nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
    1325             :                                 nsIRequest* aRequest,
    1326             :                                 nsresult aStatus,
    1327             :                                 const char16_t* aMessage)
    1328             : {
    1329          20 :   NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_STATUS,
    1330             :     listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
    1331             :   );
    1332             : 
    1333             :   // Pass the notification up to the parent...
    1334          20 :   if (mParent) {
    1335          10 :     mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
    1336             :   }
    1337          20 : }
    1338             : 
    1339             : bool
    1340           0 : nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress,
    1341             :                               nsIURI *aURI,
    1342             :                               int32_t aDelay,
    1343             :                               bool aSameURI)
    1344             : {
    1345             :   /*
    1346             :    * Returns true if the refresh may proceed,
    1347             :    * false if the refresh should be blocked.
    1348             :    */
    1349           0 :   bool allowRefresh = true;
    1350             : 
    1351           0 :   NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_REFRESH,
    1352             :     nsCOMPtr<nsIWebProgressListener2> listener2 =
    1353             :       do_QueryReferent(info.mWeakListener);
    1354             :     if (!listener2)
    1355             :       continue;
    1356             : 
    1357             :     bool listenerAllowedRefresh;
    1358             :     nsresult listenerRV = listener2->OnRefreshAttempted(
    1359             :         aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
    1360             :     if (NS_FAILED(listenerRV))
    1361             :       continue;
    1362             : 
    1363             :     allowRefresh = allowRefresh && listenerAllowedRefresh;
    1364             :   );
    1365             : 
    1366             :   // Pass the notification up to the parent...
    1367           0 :   if (mParent) {
    1368           0 :     allowRefresh = allowRefresh &&
    1369           0 :       mParent->RefreshAttempted(aWebProgress, aURI, aDelay, aSameURI);
    1370             :   }
    1371             : 
    1372           0 :   return allowRefresh;
    1373             : }
    1374             : 
    1375          76 : nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
    1376             : {
    1377          76 :   if (!mRequestInfoHash.Add(aRequest, mozilla::fallible)) {
    1378           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1379             :   }
    1380             : 
    1381          76 :   return NS_OK;
    1382             : }
    1383             : 
    1384          76 : void nsDocLoader::RemoveRequestInfo(nsIRequest *aRequest)
    1385             : {
    1386          76 :   mRequestInfoHash.Remove(aRequest);
    1387          76 : }
    1388             : 
    1389         140 : nsDocLoader::nsRequestInfo* nsDocLoader::GetRequestInfo(nsIRequest* aRequest)
    1390             : {
    1391         140 :   return static_cast<nsRequestInfo*>(mRequestInfoHash.Search(aRequest));
    1392             : }
    1393             : 
    1394          21 : void nsDocLoader::ClearRequestInfoHash(void)
    1395             : {
    1396          21 :   mRequestInfoHash.Clear();
    1397          21 : }
    1398             : 
    1399           1 : int64_t nsDocLoader::CalculateMaxProgress()
    1400             : {
    1401           1 :   int64_t max = mCompletedTotalProgress;
    1402           2 :   for (auto iter = mRequestInfoHash.Iter(); !iter.Done(); iter.Next()) {
    1403           1 :     auto info = static_cast<const nsRequestInfo*>(iter.Get());
    1404             : 
    1405           1 :     if (info->mMaxProgress < info->mCurrentProgress) {
    1406           0 :       return int64_t(-1);
    1407             :     }
    1408           1 :     max += info->mMaxProgress;
    1409             :   }
    1410           1 :   return max;
    1411             : }
    1412             : 
    1413           0 : NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
    1414             :                                                   nsIChannel *aNewChannel,
    1415             :                                                   uint32_t aFlags,
    1416             :                                                   nsIAsyncVerifyRedirectCallback *cb)
    1417             : {
    1418           0 :   if (aOldChannel)
    1419             :   {
    1420           0 :     nsLoadFlags loadFlags = 0;
    1421             :     int32_t stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
    1422           0 :                          nsIWebProgressListener::STATE_IS_REQUEST;
    1423             : 
    1424           0 :     aOldChannel->GetLoadFlags(&loadFlags);
    1425             :     // If the document channel is being redirected, then indicate that the
    1426             :     // document is being redirected in the notification...
    1427           0 :     if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
    1428             :     {
    1429           0 :       stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
    1430             : 
    1431             : #if defined(DEBUG)
    1432           0 :       nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
    1433           0 :       NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
    1434             : #endif /* DEBUG */
    1435             :     }
    1436             : 
    1437           0 :     OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
    1438           0 :     FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
    1439             :   }
    1440             : 
    1441           0 :   cb->OnRedirectVerifyCallback(NS_OK);
    1442           0 :   return NS_OK;
    1443             : }
    1444             : 
    1445             : /*
    1446             :  * Implementation of nsISecurityEventSink method...
    1447             :  */
    1448             : 
    1449           2 : NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
    1450             :                                             uint32_t aState)
    1451             : {
    1452             :   //
    1453             :   // Fire progress notifications out to any registered nsIWebProgressListeners.
    1454             :   //
    1455             : 
    1456           4 :   nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
    1457           2 :   nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
    1458             : 
    1459           2 :   NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_SECURITY,
    1460             :     listener->OnSecurityChange(webProgress, request, aState);
    1461             :   );
    1462             : 
    1463             :   // Pass the notification up to the parent...
    1464           2 :   if (mParent) {
    1465           1 :     mParent->OnSecurityChange(aContext, aState);
    1466             :   }
    1467           4 :   return NS_OK;
    1468             : }
    1469             : 
    1470             : /*
    1471             :  * Implementation of nsISupportsPriority methods...
    1472             :  *
    1473             :  * The priority of the DocLoader _is_ the priority of its LoadGroup.
    1474             :  *
    1475             :  * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
    1476             :  * go away.
    1477             :  */
    1478             : 
    1479           0 : NS_IMETHODIMP nsDocLoader::GetPriority(int32_t *aPriority)
    1480             : {
    1481           0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
    1482           0 :   if (p)
    1483           0 :     return p->GetPriority(aPriority);
    1484             : 
    1485           0 :   *aPriority = 0;
    1486           0 :   return NS_OK;
    1487             : }
    1488             : 
    1489           0 : NS_IMETHODIMP nsDocLoader::SetPriority(int32_t aPriority)
    1490             : {
    1491           0 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
    1492             :          ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
    1493             : 
    1494           0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
    1495           0 :   if (p)
    1496           0 :     p->SetPriority(aPriority);
    1497             : 
    1498           0 :   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
    1499             :                                            SetPriority, (aPriority));
    1500             : 
    1501           0 :   return NS_OK;
    1502             : }
    1503             : 
    1504           0 : NS_IMETHODIMP nsDocLoader::AdjustPriority(int32_t aDelta)
    1505             : {
    1506           0 :   MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
    1507             :          ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
    1508             : 
    1509           0 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
    1510           0 :   if (p)
    1511           0 :     p->AdjustPriority(aDelta);
    1512             : 
    1513           0 :   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
    1514             :                                            AdjustPriority, (aDelta));
    1515             : 
    1516           0 :   return NS_OK;
    1517             : }
    1518             : 
    1519             : 
    1520             : 
    1521             : 
    1522             : #if 0
    1523             : void nsDocLoader::DumpChannelInfo()
    1524             : {
    1525             :   nsChannelInfo *info;
    1526             :   int32_t i, count;
    1527             :   int32_t current=0, max=0;
    1528             : 
    1529             : 
    1530             :   printf("==== DocLoader=%x\n", this);
    1531             : 
    1532             :   count = mChannelInfoList.Count();
    1533             :   for(i=0; i<count; i++) {
    1534             :     info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
    1535             : 
    1536             : #if defined(DEBUG)
    1537             :     nsAutoCString buffer;
    1538             :     nsresult rv = NS_OK;
    1539             :     if (info->mURI) {
    1540             :       rv = info->mURI->GetSpec(buffer);
    1541             :     }
    1542             : 
    1543             :     printf("  [%d] current=%d  max=%d [%s]\n", i,
    1544             :            info->mCurrentProgress,
    1545             :            info->mMaxProgress, buffer.get());
    1546             : #endif /* DEBUG */
    1547             : 
    1548             :     current += info->mCurrentProgress;
    1549             :     if (max >= 0) {
    1550             :       if (info->mMaxProgress < info->mCurrentProgress) {
    1551             :         max = -1;
    1552             :       } else {
    1553             :         max += info->mMaxProgress;
    1554             :       }
    1555             :     }
    1556             :   }
    1557             : 
    1558             :   printf("\nCurrent=%d   Total=%d\n====\n", current, max);
    1559             : }
    1560             : #endif /* 0 */

Generated by: LCOV version 1.13