uriloader/base/nsDocLoader.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     6 #include "nspr.h"
     7 #include "prlog.h"
     9 #include "nsDocLoader.h"
    10 #include "nsCURILoader.h"
    11 #include "nsNetUtil.h"
    12 #include "nsIHttpChannel.h"
    13 #include "nsIWebProgressListener2.h"
    15 #include "nsIServiceManager.h"
    16 #include "nsXPIDLString.h"
    18 #include "nsIURL.h"
    19 #include "nsCOMPtr.h"
    20 #include "nscore.h"
    21 #include "nsWeakPtr.h"
    22 #include "nsAutoPtr.h"
    24 #include "nsIDOMWindow.h"
    26 #include "nsIStringBundle.h"
    27 #include "nsIScriptSecurityManager.h"
    29 #include "nsITransport.h"
    30 #include "nsISocketTransport.h"
    32 #include "nsIDOMDocument.h"
    33 #include "nsIDocument.h"
    34 #include "nsPresContext.h"
    35 #include "nsIAsyncVerifyRedirectCallback.h"
    37 static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
    39 #if defined(PR_LOGGING)
    40 //
    41 // Log module for nsIDocumentLoader logging...
    42 //
    43 // To enable logging (see prlog.h for full details):
    44 //
    45 //    set NSPR_LOG_MODULES=DocLoader:5
    46 //    set NSPR_LOG_FILE=nspr.log
    47 //
    48 // this enables PR_LOG_DEBUG level information and places all output in
    49 // the file nspr.log
    50 //
    51 PRLogModuleInfo* gDocLoaderLog = nullptr;
    52 #endif /* PR_LOGGING */
    55 #if defined(DEBUG)
    56 void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
    57 {
    58     if (request)
    59         request->GetName(name);
    60     else
    61         name.AssignLiteral("???");
    62 }
    63 #endif /* DEBUG */
    67 bool
    68 nsDocLoader::RequestInfoHashInitEntry(PLDHashTable* table,
    69                                       PLDHashEntryHdr* entry,
    70                                       const void* key)
    71 {
    72   // Initialize the entry with placement new
    73   new (entry) nsRequestInfo(key);
    74   return true;
    75 }
    77 void
    78 nsDocLoader::RequestInfoHashClearEntry(PLDHashTable* table,
    79                                        PLDHashEntryHdr* entry)
    80 {
    81   nsRequestInfo* info = static_cast<nsRequestInfo *>(entry);
    82   info->~nsRequestInfo();
    83 }
    85 struct nsListenerInfo {
    86   nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask) 
    87     : mWeakListener(aListener),
    88       mNotifyMask(aNotifyMask)
    89   {
    90   }
    92   // Weak pointer for the nsIWebProgressListener...
    93   nsWeakPtr mWeakListener;
    95   // Mask indicating which notifications the listener wants to receive.
    96   unsigned long mNotifyMask;
    97 };
   100 nsDocLoader::nsDocLoader()
   101   : mParent(nullptr),
   102     mListenerInfoList(8),
   103     mCurrentSelfProgress(0),
   104     mMaxSelfProgress(0),
   105     mCurrentTotalProgress(0),
   106     mMaxTotalProgress(0),
   107     mCompletedTotalProgress(0),
   108     mIsLoadingDocument(false),
   109     mIsRestoringDocument(false),
   110     mDontFlushLayout(false),
   111     mIsFlushingLayout(false)
   112 {
   113 #if defined(PR_LOGGING)
   114   if (nullptr == gDocLoaderLog) {
   115       gDocLoaderLog = PR_NewLogModule("DocLoader");
   116   }
   117 #endif /* PR_LOGGING */
   119   static const PLDHashTableOps hash_table_ops =
   120   {
   121     PL_DHashAllocTable,
   122     PL_DHashFreeTable,
   123     PL_DHashVoidPtrKeyStub,
   124     PL_DHashMatchEntryStub,
   125     PL_DHashMoveEntryStub,
   126     RequestInfoHashClearEntry,
   127     PL_DHashFinalizeStub,
   128     RequestInfoHashInitEntry
   129   };
   131   PL_DHashTableInit(&mRequestInfoHash, &hash_table_ops, nullptr,
   132                     sizeof(nsRequestInfo), 16);
   134   ClearInternalProgress();
   136   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   137          ("DocLoader:%p: created.\n", this));
   138 }
   140 nsresult
   141 nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
   142 {
   143   mParent = aParent;
   144   return NS_OK; 
   145 }
   147 nsresult
   148 nsDocLoader::Init()
   149 {
   150   if (!mRequestInfoHash.ops) {
   151     return NS_ERROR_OUT_OF_MEMORY;
   152   }
   154   nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
   155   if (NS_FAILED(rv)) return rv;
   157   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   158          ("DocLoader:%p: load group %x.\n", this, mLoadGroup.get()));
   160   return NS_OK;
   161 }
   163 nsDocLoader::~nsDocLoader()
   164 {
   165 		/*
   166 			|ClearWeakReferences()| here is intended to prevent people holding weak references
   167 			from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
   168 			subsequent |Release()| will try to destroy me.  At this point there should be only
   169 			weak references remaining (otherwise, we wouldn't be getting destroyed).
   171 			An alternative would be incrementing our refcount (consider it a compressed flag
   172 			saying "Don't re-destroy.").  I haven't yet decided which is better. [scc]
   173 		*/
   174   // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
   175   // this needed?
   176   ClearWeakReferences();
   178   Destroy();
   180   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
   181          ("DocLoader:%p: deleted.\n", this));
   183   if (mRequestInfoHash.ops) {
   184     PL_DHashTableFinish(&mRequestInfoHash);
   185   }
   186 }
   189 /*
   190  * Implementation of ISupports methods...
   191  */
   192 NS_IMPL_ADDREF(nsDocLoader)
   193 NS_IMPL_RELEASE(nsDocLoader)
   195 NS_INTERFACE_MAP_BEGIN(nsDocLoader)
   196    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
   197    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   198    NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
   199    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   200    NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
   201    NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)   
   202    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   203    NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   204    NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
   205    NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
   206    if (aIID.Equals(kThisImplCID))
   207      foundInterface = static_cast<nsIDocumentLoader *>(this);
   208    else
   209 NS_INTERFACE_MAP_END
   212 /*
   213  * Implementation of nsIInterfaceRequestor methods...
   214  */
   215 NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
   216 {
   217   nsresult rv = NS_ERROR_NO_INTERFACE;
   219   NS_ENSURE_ARG_POINTER(aSink);
   221   if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
   222     *aSink = mLoadGroup;
   223     NS_IF_ADDREF((nsISupports*)*aSink);
   224     rv = NS_OK;
   225   } else {
   226     rv = QueryInterface(aIID, aSink);
   227   }
   229   return rv;
   230 }
   232 /* static */
   233 already_AddRefed<nsDocLoader>
   234 nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
   235 {
   236   nsRefPtr<nsDocLoader> ret = do_QueryObject(aSupports);
   237   return ret.forget();
   238 }
   240 /* static */
   241 nsresult
   242 nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
   243 {
   244   nsresult rv;
   245   nsCOMPtr<nsIDocumentLoader> docLoaderService =
   246     do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
   247   NS_ENSURE_SUCCESS(rv, rv);
   249   nsRefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
   250   NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
   252   return rootDocLoader->AddChildLoader(aDocLoader);
   253 }
   255 NS_IMETHODIMP
   256 nsDocLoader::Stop(void)
   257 {
   258   nsresult rv = NS_OK;
   260   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   261          ("DocLoader:%p: Stop() called\n", this));
   263   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader, Stop, ());
   265   if (mLoadGroup)
   266     rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
   268   // Don't report that we're flushing layout so IsBusy returns false after a
   269   // Stop call.
   270   mIsFlushingLayout = false;
   272   // Clear out mChildrenInOnload.  We want to make sure to fire our
   273   // onload at this point, and there's no issue with mChildrenInOnload
   274   // after this, since mDocumentRequest will be null after the
   275   // DocLoaderIsEmpty() call.
   276   mChildrenInOnload.Clear();
   278   // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
   279   // etc, as needed.  We could be getting into here from a subframe onload, in
   280   // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
   281   // happened yet, Canceling the loadgroup did nothing (because it was already
   282   // empty), and we're about to start a new load (which is what triggered this
   283   // Stop() call).
   285   // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
   286   // we wouldn't need the call here....
   288   NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
   289   DocLoaderIsEmpty(false);
   291   return rv;
   292 }       
   295 bool
   296 nsDocLoader::IsBusy()
   297 {
   298   nsresult rv;
   300   //
   301   // A document loader is busy if either:
   302   //
   303   //   1. One of its children is in the middle of an onload handler.  Note that
   304   //      the handler may have already removed this child from mChildList!
   305   //   2. It is currently loading a document and either has parts of it still
   306   //      loading, or has a busy child docloader.
   307   //   3. It's currently flushing layout in DocLoaderIsEmpty().
   308   //
   310   if (mChildrenInOnload.Count() || mIsFlushingLayout) {
   311     return true;
   312   }
   314   /* Is this document loader busy? */
   315   if (!mIsLoadingDocument) {
   316     return false;
   317   }
   319   bool busy;
   320   rv = mLoadGroup->IsPending(&busy);
   321   if (NS_FAILED(rv)) {
   322     return false;
   323   }
   324   if (busy) {
   325     return true;
   326   }
   328   /* check its child document loaders... */
   329   uint32_t count = mChildList.Length();
   330   for (uint32_t i=0; i < count; i++) {
   331     nsIDocumentLoader* loader = ChildAt(i);
   333     // This is a safe cast, because we only put nsDocLoader objects into the
   334     // array
   335     if (loader && static_cast<nsDocLoader*>(loader)->IsBusy())
   336       return true;
   337   }
   339   return false;
   340 }
   342 NS_IMETHODIMP
   343 nsDocLoader::GetContainer(nsISupports** aResult)
   344 {
   345    NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
   347    return NS_OK;
   348 }
   350 NS_IMETHODIMP
   351 nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
   352 {
   353   nsresult rv = NS_OK;
   355   if (nullptr == aResult) {
   356     rv = NS_ERROR_NULL_POINTER;
   357   } else {
   358     *aResult = mLoadGroup;
   359     NS_IF_ADDREF(*aResult);
   360   }
   361   return rv;
   362 }
   364 void
   365 nsDocLoader::Destroy()
   366 {
   367   Stop();
   369   // Remove the document loader from the parent list of loaders...
   370   if (mParent) 
   371   {
   372     mParent->RemoveChildLoader(this);
   373   }
   375   // Release all the information about network requests...
   376   ClearRequestInfoHash();
   378   // Release all the information about registered listeners...
   379   int32_t count = mListenerInfoList.Count();
   380   for(int32_t i = 0; i < count; i++) {
   381     nsListenerInfo *info =
   382       static_cast<nsListenerInfo*>(mListenerInfoList.ElementAt(i));
   384     delete info;
   385   }
   387   mListenerInfoList.Clear();
   388   mListenerInfoList.Compact();
   390   mDocumentRequest = 0;
   392   if (mLoadGroup)
   393     mLoadGroup->SetGroupObserver(nullptr);
   395   DestroyChildren();
   396 }
   398 void
   399 nsDocLoader::DestroyChildren()
   400 {
   401   uint32_t count = mChildList.Length();
   402   // if the doc loader still has children...we need to enumerate the
   403   // children and make them null out their back ptr to the parent doc
   404   // loader
   405   for (uint32_t i=0; i < count; i++)
   406   {
   407     nsIDocumentLoader* loader = ChildAt(i);
   409     if (loader) {
   410       // This is a safe cast, as we only put nsDocLoader objects into the
   411       // array
   412       static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nullptr);
   413     }
   414   }
   415   mChildList.Clear();
   416 }
   418 NS_IMETHODIMP
   419 nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
   420 {
   421   // called each time a request is added to the group.
   423 #ifdef PR_LOGGING
   424   if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
   425     nsAutoCString name;
   426     request->GetName(name);
   428     uint32_t count = 0;
   429     if (mLoadGroup)
   430       mLoadGroup->GetActiveCount(&count);
   432     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
   433            ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
   434             this, request, name.get(),
   435             (mIsLoadingDocument ? "true" : "false"),
   436             count));
   437   }
   438 #endif /* PR_LOGGING */
   439   bool bJustStartedLoading = false;
   441   nsLoadFlags loadFlags = 0;
   442   request->GetLoadFlags(&loadFlags);
   444   if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
   445       bJustStartedLoading = true;
   446       mIsLoadingDocument = true;
   447       ClearInternalProgress(); // only clear our progress if we are starting a new load....
   448   }
   450   //
   451   // Create a new nsRequestInfo for the request that is starting to
   452   // load...
   453   //
   454   AddRequestInfo(request);
   456   //
   457   // Only fire a doStartDocumentLoad(...) if the document loader
   458   // has initiated a load...  Otherwise, this notification has
   459   // resulted from a request being added to the load group.
   460   //
   461   if (mIsLoadingDocument) {
   462     if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
   463       //
   464       // Make sure that the document channel is null at this point...
   465       // (unless its been redirected)
   466       //
   467       NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
   468                    !(mDocumentRequest.get()),
   469                    "Overwriting an existing document channel!");
   471       // This request is associated with the entire document...
   472       mDocumentRequest = request;
   473       mLoadGroup->SetDefaultLoadRequest(request); 
   475       // Only fire the start document load notification for the first
   476       // document URI...  Do not fire it again for redirections
   477       //
   478       if (bJustStartedLoading) {
   479         // Update the progress status state
   480         mProgressStateFlags = nsIWebProgressListener::STATE_START;
   482         // Fire the start document load notification
   483         doStartDocumentLoad();
   484         return NS_OK;
   485       }
   486     } 
   487   }
   489   NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
   490                "mDocumentRequest MUST be set for the duration of a page load!");
   492   doStartURLLoad(request);
   494   return NS_OK;
   495 }
   497 NS_IMETHODIMP
   498 nsDocLoader::OnStopRequest(nsIRequest *aRequest, 
   499                            nsISupports *aCtxt,
   500                            nsresult aStatus)
   501 {
   502   nsresult rv = NS_OK;
   504 #ifdef PR_LOGGING
   505   if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
   506     nsAutoCString name;
   507     aRequest->GetName(name);
   509     uint32_t count = 0;
   510     if (mLoadGroup)
   511       mLoadGroup->GetActiveCount(&count);
   513     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
   514            ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
   515            this, aRequest, name.get(),
   516            aStatus, (mIsLoadingDocument ? "true" : "false"),
   517            count));
   518   }
   519 #endif
   521   bool bFireTransferring = false;
   523   //
   524   // Set the Maximum progress to the same value as the current progress.
   525   // Since the URI has finished loading, all the data is there.  Also,
   526   // this will allow a more accurate estimation of the max progress (in case
   527   // the old value was unknown ie. -1)
   528   //
   529   nsRequestInfo *info = GetRequestInfo(aRequest);
   530   if (info) {
   531     // Null out mLastStatus now so we don't find it when looking for
   532     // status from now on.  This destroys the nsStatusInfo and hence
   533     // removes it from our list.
   534     info->mLastStatus = nullptr;
   536     int64_t oldMax = info->mMaxProgress;
   538     info->mMaxProgress = info->mCurrentProgress;
   540     //
   541     // If a request whose content-length was previously unknown has just
   542     // finished loading, then use this new data to try to calculate a
   543     // mMaxSelfProgress...
   544     //
   545     if ((oldMax < int64_t(0)) && (mMaxSelfProgress < int64_t(0))) {
   546       mMaxSelfProgress = CalculateMaxProgress();
   547     }
   549     // As we know the total progress of this request now, save it to be part
   550     // of CalculateMaxProgress() result. We need to remove the info from the
   551     // hash, see bug 480713.
   552     mCompletedTotalProgress += info->mMaxProgress;
   554     //
   555     // Determine whether a STATE_TRANSFERRING notification should be
   556     // 'synthesized'.
   557     //
   558     // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
   559     // nsRequestInfo::mCurrentProgress are both 0, then the
   560     // STATE_TRANSFERRING notification has not been fired yet...
   561     //
   562     if ((oldMax == 0) && (info->mCurrentProgress == 0)) {
   563       nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   565       // Only fire a TRANSFERRING notification if the request is also a
   566       // channel -- data transfer requires a nsIChannel!
   567       //
   568       if (channel) {
   569         if (NS_SUCCEEDED(aStatus)) {
   570           bFireTransferring = true;
   571         }
   572         //
   573         // If the request failed (for any reason other than being
   574         // redirected or retargeted), the TRANSFERRING notification can
   575         // still be fired if a HTTP connection was established to a server.
   576         //
   577         else if (aStatus != NS_BINDING_REDIRECTED &&
   578                  aStatus != NS_BINDING_RETARGETED) {
   579           //
   580           // Only if the load has been targeted (see bug 268483)...
   581           //
   582           uint32_t lf;
   583           channel->GetLoadFlags(&lf);
   584           if (lf & nsIChannel::LOAD_TARGETED) {
   585             nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
   586             if (httpChannel) {
   587               uint32_t responseCode;
   588               rv = httpChannel->GetResponseStatus(&responseCode);
   589               if (NS_SUCCEEDED(rv)) {
   590                 //
   591                 // A valid server status indicates that a connection was
   592                 // established to the server... So, fire the notification
   593                 // even though a failure occurred later...
   594                 //
   595                 bFireTransferring = true;
   596               }
   597             }
   598           }
   599         }
   600       }
   601     }
   602   }
   604   if (bFireTransferring) {
   605     // Send a STATE_TRANSFERRING notification for the request.
   606     int32_t flags;
   608     flags = nsIWebProgressListener::STATE_TRANSFERRING |
   609             nsIWebProgressListener::STATE_IS_REQUEST;
   610     //
   611     // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
   612     //
   613     if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
   614       mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
   616       // Send STATE_TRANSFERRING for the document too...
   617       flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
   618     }
   620     FireOnStateChange(this, aRequest, flags, NS_OK);
   621   }
   623   //
   624   // Fire the OnStateChange(...) notification for stop request
   625   //
   626   doStopURLLoad(aRequest, aStatus);
   628   // Clear this request out of the hash to avoid bypass of FireOnStateChange
   629   // when address of the request is reused.
   630   RemoveRequestInfo(aRequest);
   632   //
   633   // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
   634   // load.  This will handle removing the request from our hashtable as needed.
   635   //
   636   if (mIsLoadingDocument) {
   637     DocLoaderIsEmpty(true);
   638   }
   640   return NS_OK;
   641 }
   644 nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
   645 {
   646   nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
   647   if (NS_SUCCEEDED(rv)) {
   648     aChild->SetDocLoaderParent(nullptr);
   649   }
   650   return rv;
   651 }
   653 nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
   654 {
   655   nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   656   if (NS_SUCCEEDED(rv)) {
   657     aChild->SetDocLoaderParent(this);
   658   }
   659   return rv;
   660 }
   662 NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
   663 {
   664   if (!mDocumentRequest) {
   665     *aChannel = nullptr;
   666     return NS_OK;
   667   }
   669   return CallQueryInterface(mDocumentRequest, aChannel);
   670 }
   673 void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
   674 {
   675   if (mIsLoadingDocument) {
   676     /* In the unimagineably rude circumstance that onload event handlers
   677        triggered by this function actually kill the window ... ok, it's
   678        not unimagineable; it's happened ... this deathgrip keeps this object
   679        alive long enough to survive this function call. */
   680     nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
   682     // Don't flush layout if we're still busy.
   683     if (IsBusy()) {
   684       return;
   685     }
   687     NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
   689     // The load group for this DocumentLoader is idle.  Flush if we need to.
   690     if (aFlushLayout && !mDontFlushLayout) {
   691       nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(GetAsSupports(this));
   692       nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
   693       if (doc) {
   694         // We start loads from style resolution, so we need to flush out style
   695         // no matter what.  If we have user fonts, we also need to flush layout,
   696         // since the reflow is what starts font loads.
   697         mozFlushType flushType = Flush_Style;
   698         nsIPresShell* shell = doc->GetShell();
   699         if (shell) {
   700           // Be safe in case this presshell is in teardown now
   701           nsPresContext* presContext = shell->GetPresContext();
   702           if (presContext && presContext->GetUserFontSet()) {
   703             flushType = Flush_Layout;
   704           }
   705         }
   706         mDontFlushLayout = mIsFlushingLayout = true;
   707         doc->FlushPendingNotifications(flushType);
   708         mDontFlushLayout = mIsFlushingLayout = false;
   709       }
   710     }
   712     // And now check whether we're really busy; that might have changed with
   713     // the layout flush.
   714     if (!IsBusy()) {
   715       // Clear out our request info hash, now that our load really is done and
   716       // we don't need it anymore to CalculateMaxProgress().
   717       ClearInternalProgress();
   719       PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   720              ("DocLoader:%p: Is now idle...\n", this));
   722       nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
   724       NS_ASSERTION(mDocumentRequest, "No Document Request!");
   725       mDocumentRequest = 0;
   726       mIsLoadingDocument = false;
   728       // Update the progress status state - the document is done
   729       mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
   732       nsresult loadGroupStatus = NS_OK; 
   733       mLoadGroup->GetStatus(&loadGroupStatus);
   735       // 
   736       // New code to break the circular reference between 
   737       // the load group and the docloader... 
   738       // 
   739       mLoadGroup->SetDefaultLoadRequest(nullptr); 
   741       // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
   742       // it even if our onload handler removes us from the docloader tree.
   743       nsRefPtr<nsDocLoader> parent = mParent;
   745       // Note that if calling ChildEnteringOnload() on the parent returns false
   746       // then calling our onload handler is not safe.  That can only happen on
   747       // OOM, so that's ok.
   748       if (!parent || parent->ChildEnteringOnload(this)) {
   749         // Do nothing with our state after firing the
   750         // OnEndDocumentLoad(...). The document loader may be loading a *new*
   751         // document - if LoadDocument() was called from a handler!
   752         //
   753         doStopDocumentLoad(docRequest, loadGroupStatus);
   755         if (parent) {
   756           parent->ChildDoneWithOnload(this);
   757         }
   758       }
   759     }
   760   }
   761 }
   763 void nsDocLoader::doStartDocumentLoad(void)
   764 {
   766 #if defined(DEBUG)
   767   nsAutoCString buffer;
   769   GetURIStringFromRequest(mDocumentRequest, buffer);
   770   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   771          ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
   772           "\tURI: %s \n",
   773           this, buffer.get()));
   774 #endif /* DEBUG */
   776   // Fire an OnStatus(...) notification STATE_START.  This indicates
   777   // that the document represented by mDocumentRequest has started to
   778   // load...
   779   FireOnStateChange(this,
   780                     mDocumentRequest,
   781                     nsIWebProgressListener::STATE_START |
   782                     nsIWebProgressListener::STATE_IS_DOCUMENT |
   783                     nsIWebProgressListener::STATE_IS_REQUEST |
   784                     nsIWebProgressListener::STATE_IS_WINDOW |
   785                     nsIWebProgressListener::STATE_IS_NETWORK,
   786                     NS_OK);
   787 }
   789 void nsDocLoader::doStartURLLoad(nsIRequest *request)
   790 {
   791 #if defined(DEBUG)
   792   nsAutoCString buffer;
   794   GetURIStringFromRequest(request, buffer);
   795     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   796           ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
   797            "\tURI: %s\n",
   798             this, buffer.get()));
   799 #endif /* DEBUG */
   801   FireOnStateChange(this,
   802                     request,
   803                     nsIWebProgressListener::STATE_START |
   804                     nsIWebProgressListener::STATE_IS_REQUEST,
   805                     NS_OK);
   806 }
   808 void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
   809 {
   810 #if defined(DEBUG)
   811   nsAutoCString buffer;
   813   GetURIStringFromRequest(request, buffer);
   814     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   815           ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
   816            "\tURI: %s status=%x\n",
   817             this, buffer.get(), aStatus));
   818 #endif /* DEBUG */
   820   FireOnStateChange(this,
   821                     request,
   822                     nsIWebProgressListener::STATE_STOP |
   823                     nsIWebProgressListener::STATE_IS_REQUEST,
   824                     aStatus);
   826   // Fire a status change message for the most recent unfinished
   827   // request to make sure that the displayed status is not outdated.
   828   if (!mStatusInfoList.isEmpty()) {
   829     nsStatusInfo* statusInfo = mStatusInfoList.getFirst();
   830     FireOnStatusChange(this, statusInfo->mRequest,
   831                        statusInfo->mStatusCode,
   832                        statusInfo->mStatusMessage.get());
   833   }
   834 }
   836 void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
   837                                          nsresult aStatus)
   838 {
   839 #if defined(DEBUG)
   840   nsAutoCString buffer;
   842   GetURIStringFromRequest(request, buffer);
   843   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
   844          ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
   845          "\tURI: %s Status=%x\n",
   846           this, buffer.get(), aStatus));
   847 #endif /* DEBUG */
   849   // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
   850   // Grab our parent chain before doing that so we can still dispatch
   851   // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
   852   // the onload handlers rearrange the docshell tree.
   853   WebProgressList list;
   854   GatherAncestorWebProgresses(list);
   856   //
   857   // Fire an OnStateChange(...) notification indicating the the
   858   // current document has finished loading...
   859   //
   860   int32_t flags = nsIWebProgressListener::STATE_STOP |
   861                   nsIWebProgressListener::STATE_IS_DOCUMENT;
   862   for (uint32_t i = 0; i < list.Length(); ++i) {
   863     list[i]->DoFireOnStateChange(this, request, flags, aStatus);
   864   }
   866   //
   867   // Fire a final OnStateChange(...) notification indicating the the
   868   // current document has finished loading...
   869   //
   870   flags = nsIWebProgressListener::STATE_STOP |
   871           nsIWebProgressListener::STATE_IS_WINDOW |
   872           nsIWebProgressListener::STATE_IS_NETWORK;
   873   for (uint32_t i = 0; i < list.Length(); ++i) {
   874     list[i]->DoFireOnStateChange(this, request, flags, aStatus);
   875   }
   876 }
   878 ////////////////////////////////////////////////////////////////////////////////////
   879 // The following section contains support for nsIWebProgress and related stuff
   880 ////////////////////////////////////////////////////////////////////////////////////
   882 NS_IMETHODIMP
   883 nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
   884                                      uint32_t aNotifyMask)
   885 {
   886   nsresult rv;
   888   nsListenerInfo* info = GetListenerInfo(aListener);
   889   if (info) {
   890     // The listener is already registered!
   891     return NS_ERROR_FAILURE;
   892   }
   894   nsWeakPtr listener = do_GetWeakReference(aListener);
   895   if (!listener) {
   896     return NS_ERROR_INVALID_ARG;
   897   }
   899   info = new nsListenerInfo(listener, aNotifyMask);
   900   if (!info) {
   901     return NS_ERROR_OUT_OF_MEMORY;
   902   }
   904   rv = mListenerInfoList.AppendElement(info) ? NS_OK : NS_ERROR_FAILURE;
   905   return rv;
   906 }
   908 NS_IMETHODIMP
   909 nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
   910 {
   911   nsresult rv;
   913   nsListenerInfo* info = GetListenerInfo(aListener);
   914   if (info) {
   915     rv = mListenerInfoList.RemoveElement(info) ? NS_OK : NS_ERROR_FAILURE;
   916     delete info;
   917   } else {
   918     // The listener is not registered!
   919     rv = NS_ERROR_FAILURE;
   920   }
   921   return rv;
   922 }
   924 NS_IMETHODIMP
   925 nsDocLoader::GetDOMWindow(nsIDOMWindow **aResult)
   926 {
   927   return CallGetInterface(this, aResult);
   928 }
   930 NS_IMETHODIMP
   931 nsDocLoader::GetDOMWindowID(uint64_t *aResult)
   932 {
   933   *aResult = 0;
   935   nsCOMPtr<nsIDOMWindow> window;
   936   nsresult rv = GetDOMWindow(getter_AddRefs(window));
   937   NS_ENSURE_SUCCESS(rv, rv);
   939   nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(window);
   940   NS_ENSURE_STATE(piwindow);
   942   MOZ_ASSERT(piwindow->IsOuterWindow());
   943   *aResult = piwindow->WindowID();
   944   return NS_OK;
   945 }
   947 NS_IMETHODIMP
   948 nsDocLoader::GetIsTopLevel(bool *aResult)
   949 {
   950   *aResult = false;
   952   nsCOMPtr<nsIDOMWindow> window;
   953   GetDOMWindow(getter_AddRefs(window));
   954   if (window) {
   955     nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(window);
   956     NS_ENSURE_STATE(piwindow);
   958     nsCOMPtr<nsIDOMWindow> topWindow;
   959     nsresult rv = piwindow->GetTop(getter_AddRefs(topWindow));
   960     NS_ENSURE_SUCCESS(rv, rv);
   962     *aResult = piwindow == topWindow;
   963   }
   965   return NS_OK;
   966 }
   968 NS_IMETHODIMP
   969 nsDocLoader::GetIsLoadingDocument(bool *aIsLoadingDocument)
   970 {
   971   *aIsLoadingDocument = mIsLoadingDocument;
   973   return NS_OK;
   974 }
   976 NS_IMETHODIMP
   977 nsDocLoader::GetLoadType(uint32_t *aLoadType)
   978 {
   979   *aLoadType = 0;
   981   return NS_ERROR_NOT_IMPLEMENTED;
   982 }
   984 int64_t nsDocLoader::GetMaxTotalProgress()
   985 {
   986   int64_t newMaxTotal = 0;
   988   uint32_t count = mChildList.Length();
   989   nsCOMPtr<nsIWebProgress> webProgress;
   990   for (uint32_t i=0; i < count; i++) 
   991   {
   992     int64_t individualProgress = 0;
   993     nsIDocumentLoader* docloader = ChildAt(i);
   994     if (docloader)
   995     {
   996       // Cast is safe since all children are nsDocLoader too
   997       individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
   998     }
   999     if (individualProgress < int64_t(0)) // if one of the elements doesn't know it's size
  1000                                          // then none of them do
  1002        newMaxTotal = int64_t(-1);
  1003        break;
  1005     else
  1006      newMaxTotal += individualProgress;
  1009   int64_t progress = -1;
  1010   if (mMaxSelfProgress >= int64_t(0) && newMaxTotal >= int64_t(0))
  1011     progress = newMaxTotal + mMaxSelfProgress;
  1013   return progress;
  1016 ////////////////////////////////////////////////////////////////////////////////////
  1017 // The following section contains support for nsIProgressEventSink which is used to 
  1018 // pass progress and status between the actual request and the doc loader. The doc loader
  1019 // then turns around and makes the right web progress calls based on this information.
  1020 ////////////////////////////////////////////////////////////////////////////////////
  1022 NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt, 
  1023                                       uint64_t aProgress, uint64_t aProgressMax)
  1025   int64_t progressDelta = 0;
  1027   //
  1028   // Update the RequestInfo entry with the new progress data
  1029   //
  1030   if (nsRequestInfo* info = GetRequestInfo(aRequest)) {
  1031     // Update info->mCurrentProgress before we call FireOnStateChange,
  1032     // since that can make the "info" pointer invalid.
  1033     int64_t oldCurrentProgress = info->mCurrentProgress;
  1034     progressDelta = int64_t(aProgress) - oldCurrentProgress;
  1035     info->mCurrentProgress = int64_t(aProgress);
  1037     // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
  1038     if (!info->mUploading && (int64_t(0) == oldCurrentProgress) && (int64_t(0) == info->mMaxProgress)) {
  1039       //
  1040       // If we receive an OnProgress event from a toplevel channel that the URI Loader
  1041       // has not yet targeted, then we must suppress the event.  This is necessary to
  1042       // ensure that webprogresslisteners do not get confused when the channel is
  1043       // finally targeted.  See bug 257308.
  1044       //
  1045       nsLoadFlags lf = 0;
  1046       aRequest->GetLoadFlags(&lf);
  1047       if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
  1048         PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
  1049             ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
  1050         return NS_OK;
  1053       //
  1054       // This is the first progress notification for the entry.  If
  1055       // (aMaxProgress > 0) then the content-length of the data is known,
  1056       // so update mMaxSelfProgress...  Otherwise, set it to -1 to indicate
  1057       // that the content-length is no longer known.
  1058       //
  1059       if (uint64_t(aProgressMax) != UINT64_MAX) {
  1060         mMaxSelfProgress  += int64_t(aProgressMax);
  1061         info->mMaxProgress = int64_t(aProgressMax);
  1062       } else {
  1063         mMaxSelfProgress   =  int64_t(-1);
  1064         info->mMaxProgress =  int64_t(-1);
  1067       // Send a STATE_TRANSFERRING notification for the request.
  1068       int32_t flags;
  1070       flags = nsIWebProgressListener::STATE_TRANSFERRING | 
  1071               nsIWebProgressListener::STATE_IS_REQUEST;
  1072       //
  1073       // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
  1074       //
  1075       if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
  1076         mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
  1078         // Send STATE_TRANSFERRING for the document too...
  1079         flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
  1082       FireOnStateChange(this, aRequest, flags, NS_OK);
  1085     // Update our overall current progress count.
  1086     mCurrentSelfProgress += progressDelta;
  1088   //
  1089   // The request is not part of the load group, so ignore its progress
  1090   // information...
  1091   //
  1092   else {
  1093 #if defined(DEBUG)
  1094     nsAutoCString buffer;
  1096     GetURIStringFromRequest(aRequest, buffer);
  1097     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
  1098            ("DocLoader:%p OOPS - No Request Info for: %s\n",
  1099             this, buffer.get()));
  1100 #endif /* DEBUG */
  1102     return NS_OK;
  1105   //
  1106   // Fire progress notifications out to any registered nsIWebProgressListeners
  1107   //
  1108   FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
  1109                        mCurrentTotalProgress, mMaxTotalProgress);
  1111   return NS_OK;
  1114 NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt, 
  1115                                         nsresult aStatus, const char16_t* aStatusArg)
  1117   //
  1118   // Fire progress notifications out to any registered nsIWebProgressListeners
  1119   //
  1120   if (aStatus != NS_OK) {
  1121     // Remember the current status for this request
  1122     nsRequestInfo *info;
  1123     info = GetRequestInfo(aRequest);
  1124     if (info) {
  1125       bool uploading = (aStatus == NS_NET_STATUS_WRITING ||
  1126                         aStatus == NS_NET_STATUS_SENDING_TO);
  1127       // If switching from uploading to downloading (or vice versa), then we
  1128       // need to reset our progress counts.  This is designed with HTTP form
  1129       // submission in mind, where an upload is performed followed by download
  1130       // of possibly several documents.
  1131       if (info->mUploading != uploading) {
  1132         mCurrentSelfProgress  = mMaxSelfProgress  = 0;
  1133         mCurrentTotalProgress = mMaxTotalProgress = 0;
  1134         mCompletedTotalProgress = 0;
  1135         info->mUploading = uploading;
  1136         info->mCurrentProgress = 0;
  1137         info->mMaxProgress = 0;
  1141     nsCOMPtr<nsIStringBundleService> sbs =
  1142       mozilla::services::GetStringBundleService();
  1143     if (!sbs)
  1144       return NS_ERROR_FAILURE;
  1145     nsXPIDLString msg;
  1146     nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg,
  1147                                            getter_Copies(msg));
  1148     if (NS_FAILED(rv))
  1149       return rv;
  1151     // Keep around the message. In case a request finishes, we need to make sure
  1152     // to send the status message of another request to our user to that we
  1153     // don't display, for example, "Transferring" messages for requests that are
  1154     // already done.
  1155     if (info) {
  1156       if (!info->mLastStatus) {
  1157         info->mLastStatus = new nsStatusInfo(aRequest);
  1158       } else {
  1159         // We're going to move it to the front of the list, so remove
  1160         // it from wherever it is now.
  1161         info->mLastStatus->remove();
  1163       info->mLastStatus->mStatusMessage = msg;
  1164       info->mLastStatus->mStatusCode = aStatus;
  1165       // Put the info at the front of the list
  1166       mStatusInfoList.insertFront(info->mLastStatus);
  1168     FireOnStatusChange(this, aRequest, aStatus, msg);
  1170   return NS_OK;
  1173 void nsDocLoader::ClearInternalProgress()
  1175   ClearRequestInfoHash();
  1177   mCurrentSelfProgress  = mMaxSelfProgress  = 0;
  1178   mCurrentTotalProgress = mMaxTotalProgress = 0;
  1179   mCompletedTotalProgress = 0;
  1181   mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
  1185 void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
  1186                                        nsIRequest *request,
  1187                                        int64_t aProgress,
  1188                                        int64_t aProgressMax,
  1189                                        int64_t aProgressDelta,
  1190                                        int64_t aTotalProgress,
  1191                                        int64_t aMaxTotalProgress)
  1193   if (mIsLoadingDocument) {
  1194     mCurrentTotalProgress += aProgressDelta;
  1195     mMaxTotalProgress = GetMaxTotalProgress();
  1197     aTotalProgress    = mCurrentTotalProgress;
  1198     aMaxTotalProgress = mMaxTotalProgress;
  1201 #if defined(DEBUG)
  1202   nsAutoCString buffer;
  1204   GetURIStringFromRequest(request, buffer);
  1205   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
  1206          ("DocLoader:%p: Progress (%s): curSelf: %d maxSelf: %d curTotal: %d maxTotal %d\n",
  1207           this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
  1208 #endif /* DEBUG */
  1210   /*
  1211    * First notify any listeners of the new progress info...
  1213    * Operate the elements from back to front so that if items get
  1214    * get removed from the list it won't affect our iteration
  1215    */
  1216   nsCOMPtr<nsIWebProgressListener> listener;
  1217   int32_t count = mListenerInfoList.Count();
  1219   while (--count >= 0) {
  1220     nsListenerInfo *info;
  1222     info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
  1223     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_PROGRESS)) {
  1224       continue;
  1227     listener = do_QueryReferent(info->mWeakListener);
  1228     if (!listener) {
  1229       // the listener went away. gracefully pull it out of the list.
  1230       mListenerInfoList.RemoveElementAt(count);
  1231       delete info;
  1232       continue;
  1235     // XXX truncates 64-bit to 32-bit
  1236     listener->OnProgressChange(aLoadInitiator,request,
  1237                                int32_t(aProgress), int32_t(aProgressMax),
  1238                                int32_t(aTotalProgress), int32_t(aMaxTotalProgress));
  1241   mListenerInfoList.Compact();
  1243   // Pass the notification up to the parent...
  1244   if (mParent) {
  1245     mParent->FireOnProgressChange(aLoadInitiator, request,
  1246                                   aProgress, aProgressMax,
  1247                                   aProgressDelta,
  1248                                   aTotalProgress, aMaxTotalProgress);
  1252 void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList)
  1254   for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
  1255     aList.AppendElement(loader);
  1259 void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
  1260                                     nsIRequest *aRequest,
  1261                                     int32_t aStateFlags,
  1262                                     nsresult aStatus)
  1264   WebProgressList list;
  1265   GatherAncestorWebProgresses(list);
  1266   for (uint32_t i = 0; i < list.Length(); ++i) {
  1267     list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
  1271 void nsDocLoader::DoFireOnStateChange(nsIWebProgress * const aProgress,
  1272                                       nsIRequest * const aRequest,
  1273                                       int32_t &aStateFlags,
  1274                                       const nsresult aStatus)
  1276   //
  1277   // Remove the STATE_IS_NETWORK bit if necessary.
  1278   //
  1279   // The rule is to remove this bit, if the notification has been passed
  1280   // up from a child WebProgress, and the current WebProgress is already
  1281   // active...
  1282   //
  1283   if (mIsLoadingDocument &&
  1284       (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) && 
  1285       (this != aProgress)) {
  1286     aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
  1289   // Add the STATE_RESTORING bit if necessary.
  1290   if (mIsRestoringDocument)
  1291     aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
  1293 #if defined(DEBUG)
  1294   nsAutoCString buffer;
  1296   GetURIStringFromRequest(aRequest, buffer);
  1297   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
  1298          ("DocLoader:%p: Status (%s): code: %x\n",
  1299          this, buffer.get(), aStateFlags));
  1300 #endif /* DEBUG */
  1302   NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
  1304   /*                                                                           
  1305    * First notify any listeners of the new state info...
  1307    * Operate the elements from back to front so that if items get
  1308    * get removed from the list it won't affect our iteration
  1309    */
  1310   nsCOMPtr<nsIWebProgressListener> listener;
  1311   int32_t count = mListenerInfoList.Count();
  1312   int32_t notifyMask = (aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL;
  1314   while (--count >= 0) {
  1315     nsListenerInfo *info;
  1317     info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
  1318     if (!info || !(info->mNotifyMask & notifyMask)) {
  1319       continue;
  1322     listener = do_QueryReferent(info->mWeakListener);
  1323     if (!listener) {
  1324       // the listener went away. gracefully pull it out of the list.
  1325       mListenerInfoList.RemoveElementAt(count);
  1326       delete info;
  1327       continue;
  1330     listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
  1333   mListenerInfoList.Compact();
  1338 void
  1339 nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
  1340                                   nsIRequest* aRequest,
  1341                                   nsIURI *aUri,
  1342                                   uint32_t aFlags)
  1344   /*                                                                           
  1345    * First notify any listeners of the new state info...
  1347    * Operate the elements from back to front so that if items get
  1348    * get removed from the list it won't affect our iteration
  1349    */
  1350   nsCOMPtr<nsIWebProgressListener> listener;
  1351   int32_t count = mListenerInfoList.Count();
  1353   while (--count >= 0) {
  1354     nsListenerInfo *info;
  1356     info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
  1357     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_LOCATION)) {
  1358       continue;
  1361     listener = do_QueryReferent(info->mWeakListener);
  1362     if (!listener) {
  1363       // the listener went away. gracefully pull it out of the list.
  1364       mListenerInfoList.RemoveElementAt(count);
  1365       delete info;
  1366       continue;
  1369     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, ("DocLoader [%p] calling %p->OnLocationChange", this, listener.get()));
  1370     listener->OnLocationChange(aWebProgress, aRequest, aUri, aFlags);
  1373   mListenerInfoList.Compact();
  1375   // Pass the notification up to the parent...
  1376   if (mParent) {
  1377     mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
  1381 void
  1382 nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
  1383                                 nsIRequest* aRequest,
  1384                                 nsresult aStatus,
  1385                                 const char16_t* aMessage)
  1387   /*                                                                           
  1388    * First notify any listeners of the new state info...
  1390    * Operate the elements from back to front so that if items get
  1391    * get removed from the list it won't affect our iteration
  1392    */
  1393   nsCOMPtr<nsIWebProgressListener> listener;
  1394   int32_t count = mListenerInfoList.Count();
  1396   while (--count >= 0) {
  1397     nsListenerInfo *info;
  1399     info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
  1400     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_STATUS)) {
  1401       continue;
  1404     listener = do_QueryReferent(info->mWeakListener);
  1405     if (!listener) {
  1406       // the listener went away. gracefully pull it out of the list.
  1407       mListenerInfoList.RemoveElementAt(count);
  1408       delete info;
  1409       continue;
  1412     listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
  1414   mListenerInfoList.Compact();
  1416   // Pass the notification up to the parent...
  1417   if (mParent) {
  1418     mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
  1422 bool
  1423 nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress,
  1424                               nsIURI *aURI,
  1425                               int32_t aDelay,
  1426                               bool aSameURI)
  1428   /*
  1429    * Returns true if the refresh may proceed,
  1430    * false if the refresh should be blocked.
  1432    * First notify any listeners of the refresh attempt...
  1434    * Iterate the elements from back to front so that if items
  1435    * get removed from the list it won't affect our iteration
  1436    */
  1437   bool allowRefresh = true;
  1438   int32_t count = mListenerInfoList.Count();
  1440   while (--count >= 0) {
  1441     nsListenerInfo *info;
  1443     info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
  1444     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_REFRESH)) {
  1445       continue;
  1448     nsCOMPtr<nsIWebProgressListener> listener =
  1449       do_QueryReferent(info->mWeakListener);
  1450     if (!listener) {
  1451       // the listener went away. gracefully pull it out of the list.
  1452       mListenerInfoList.RemoveElementAt(count);
  1453       delete info;
  1454       continue;
  1457     nsCOMPtr<nsIWebProgressListener2> listener2 =
  1458       do_QueryReferent(info->mWeakListener);
  1459     if (!listener2)
  1460       continue;
  1462     bool listenerAllowedRefresh;
  1463     nsresult listenerRV = listener2->OnRefreshAttempted(
  1464         aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
  1465     if (NS_FAILED(listenerRV))
  1466       continue;
  1468     allowRefresh = allowRefresh && listenerAllowedRefresh;
  1471   mListenerInfoList.Compact();
  1473   // Pass the notification up to the parent...
  1474   if (mParent) {
  1475     allowRefresh = allowRefresh &&
  1476       mParent->RefreshAttempted(aWebProgress, aURI, aDelay, aSameURI);
  1479   return allowRefresh;
  1482 nsListenerInfo * 
  1483 nsDocLoader::GetListenerInfo(nsIWebProgressListener *aListener)
  1485   int32_t i, count;
  1486   nsListenerInfo *info;
  1488   nsCOMPtr<nsISupports> listener1 = do_QueryInterface(aListener);
  1489   count = mListenerInfoList.Count();
  1490   for (i=0; i<count; i++) {
  1491     info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(i));
  1493     NS_ASSERTION(info, "There should NEVER be a null listener in the list");
  1494     if (info) {
  1495       nsCOMPtr<nsISupports> listener2 = do_QueryReferent(info->mWeakListener);
  1496       if (listener1 == listener2)
  1497         return info;
  1500   return nullptr;
  1503 nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
  1505   if (!PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_ADD)) {
  1506     return NS_ERROR_OUT_OF_MEMORY;
  1509   return NS_OK;
  1512 void nsDocLoader::RemoveRequestInfo(nsIRequest *aRequest)
  1514   PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_REMOVE);
  1517 nsDocLoader::nsRequestInfo* nsDocLoader::GetRequestInfo(nsIRequest* aRequest)
  1519   nsRequestInfo* info =
  1520     static_cast<nsRequestInfo*>
  1521                (PL_DHashTableOperate(&mRequestInfoHash, aRequest,
  1522                                         PL_DHASH_LOOKUP));
  1524   if (PL_DHASH_ENTRY_IS_FREE(info)) {
  1525     // Nothing found in the hash, return null.
  1527     return nullptr;
  1530   // Return what we found in the hash...
  1532   return info;
  1535 // PLDHashTable enumeration callback that just removes every entry
  1536 // from the hash.
  1537 static PLDHashOperator
  1538 RemoveInfoCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
  1539                    void *arg)
  1541   return PL_DHASH_REMOVE;
  1544 void nsDocLoader::ClearRequestInfoHash(void)
  1546   if (!mRequestInfoHash.ops || !mRequestInfoHash.entryCount) {
  1547     // No hash, or the hash is empty, nothing to do here then...
  1549     return;
  1552   PL_DHashTableEnumerate(&mRequestInfoHash, RemoveInfoCallback, nullptr);
  1555 // PLDHashTable enumeration callback that calculates the max progress.
  1556 PLDHashOperator
  1557 nsDocLoader::CalcMaxProgressCallback(PLDHashTable* table, PLDHashEntryHdr* hdr,
  1558                                      uint32_t number, void* arg)
  1560   const nsRequestInfo* info = static_cast<const nsRequestInfo*>(hdr);
  1561   int64_t* max = static_cast<int64_t* >(arg);
  1563   if (info->mMaxProgress < info->mCurrentProgress) {
  1564     *max = int64_t(-1);
  1566     return PL_DHASH_STOP;
  1569   *max += info->mMaxProgress;
  1571   return PL_DHASH_NEXT;
  1574 int64_t nsDocLoader::CalculateMaxProgress()
  1576   int64_t max = mCompletedTotalProgress;
  1577   PL_DHashTableEnumerate(&mRequestInfoHash, CalcMaxProgressCallback, &max);
  1578   return max;
  1581 NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
  1582                                                   nsIChannel *aNewChannel,
  1583                                                   uint32_t aFlags,
  1584                                                   nsIAsyncVerifyRedirectCallback *cb)
  1586   if (aOldChannel)
  1588     nsLoadFlags loadFlags = 0;
  1589     int32_t stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
  1590                          nsIWebProgressListener::STATE_IS_REQUEST;
  1592     aOldChannel->GetLoadFlags(&loadFlags);
  1593     // If the document channel is being redirected, then indicate that the
  1594     // document is being redirected in the notification...
  1595     if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
  1597       stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
  1599 #if defined(DEBUG)
  1600       nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
  1601       NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
  1602 #endif /* DEBUG */
  1605     OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
  1606     FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
  1609   cb->OnRedirectVerifyCallback(NS_OK);
  1610   return NS_OK;
  1613 /*
  1614  * Implementation of nsISecurityEventSink method...
  1615  */
  1617 NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
  1618                                             uint32_t aState)
  1620   //
  1621   // Fire progress notifications out to any registered nsIWebProgressListeners.  
  1622   //
  1624   nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
  1625   nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
  1627   /*                                                                           
  1628    * First notify any listeners of the new state info...
  1630    * Operate the elements from back to front so that if items get
  1631    * get removed from the list it won't affect our iteration
  1632    */
  1633   nsCOMPtr<nsIWebProgressListener> listener;
  1634   int32_t count = mListenerInfoList.Count();
  1636   while (--count >= 0) {
  1637     nsListenerInfo *info;
  1639     info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
  1640     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_SECURITY)) {
  1641       continue;
  1644     listener = do_QueryReferent(info->mWeakListener);
  1645     if (!listener) {
  1646       // the listener went away. gracefully pull it out of the list.
  1647       mListenerInfoList.RemoveElementAt(count);
  1648       delete info;
  1649       continue;
  1652     listener->OnSecurityChange(webProgress, request, aState);
  1655   mListenerInfoList.Compact();
  1657   // Pass the notification up to the parent...
  1658   if (mParent) {
  1659     mParent->OnSecurityChange(aContext, aState);
  1661   return NS_OK;
  1664 /*
  1665  * Implementation of nsISupportsPriority methods...
  1667  * The priority of the DocLoader _is_ the priority of its LoadGroup.
  1669  * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
  1670  * go away. 
  1671  */
  1673 NS_IMETHODIMP nsDocLoader::GetPriority(int32_t *aPriority)
  1675   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
  1676   if (p)
  1677     return p->GetPriority(aPriority);
  1679   *aPriority = 0;
  1680   return NS_OK;
  1683 NS_IMETHODIMP nsDocLoader::SetPriority(int32_t aPriority)
  1685   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
  1686          ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
  1688   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
  1689   if (p)
  1690     p->SetPriority(aPriority);
  1692   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
  1693                                            SetPriority, (aPriority));
  1695   return NS_OK;
  1698 NS_IMETHODIMP nsDocLoader::AdjustPriority(int32_t aDelta)
  1700   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
  1701          ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
  1703   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
  1704   if (p)
  1705     p->AdjustPriority(aDelta);
  1707   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
  1708                                            AdjustPriority, (aDelta));
  1710   return NS_OK;
  1716 #if 0
  1717 void nsDocLoader::DumpChannelInfo()
  1719   nsChannelInfo *info;
  1720   int32_t i, count;
  1721   int32_t current=0, max=0;
  1724   printf("==== DocLoader=%x\n", this);
  1726   count = mChannelInfoList.Count();
  1727   for(i=0; i<count; i++) {
  1728     info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
  1730 #if defined(DEBUG)
  1731     nsAutoCString buffer;
  1732     nsresult rv = NS_OK;
  1733     if (info->mURI) {
  1734       rv = info->mURI->GetSpec(buffer);
  1737     printf("  [%d] current=%d  max=%d [%s]\n", i,
  1738            info->mCurrentProgress, 
  1739            info->mMaxProgress, buffer.get());
  1740 #endif /* DEBUG */
  1742     current += info->mCurrentProgress;
  1743     if (max >= 0) {
  1744       if (info->mMaxProgress < info->mCurrentProgress) {
  1745         max = -1;
  1746       } else {
  1747         max += info->mMaxProgress;
  1752   printf("\nCurrent=%d   Total=%d\n====\n", current, max);
  1754 #endif /* 0 */

mercurial