Back to index

lightning-sunbird  0.9+nobinonly
nsDocLoader.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nspr.h"
00039 #include "prlog.h"
00040 
00041 #include "nsDocLoader.h"
00042 #include "nsCURILoader.h"
00043 #include "nsNetUtil.h"
00044 #include "nsIHttpChannel.h"
00045 
00046 #include "nsIServiceManager.h"
00047 #include "nsXPIDLString.h"
00048 
00049 #include "nsIURL.h"
00050 #include "nsCOMPtr.h"
00051 #include "nscore.h"
00052 #include "nsWeakPtr.h"
00053 #include "nsAutoPtr.h"
00054 
00055 #include "nsIDOMWindow.h"
00056 
00057 #include "nsIPresShell.h"
00058 #include "nsPresContext.h"
00059 #include "nsIStringBundle.h"
00060 #include "nsIScriptSecurityManager.h"
00061 
00062 #include "nsITransport.h"
00063 #include "nsISocketTransport.h"
00064 
00065 static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
00066 static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
00067 
00068 #if defined(PR_LOGGING)
00069 //
00070 // Log module for nsIDocumentLoader logging...
00071 //
00072 // To enable logging (see prlog.h for full details):
00073 //
00074 //    set NSPR_LOG_MODULES=DocLoader:5
00075 //    set NSPR_LOG_FILE=nspr.log
00076 //
00077 // this enables PR_LOG_DEBUG level information and places all output in
00078 // the file nspr.log
00079 //
00080 PRLogModuleInfo* gDocLoaderLog = nsnull;
00081 #endif /* PR_LOGGING */
00082 
00083 
00084 #if defined(DEBUG)
00085 void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
00086 {
00087     if (request)
00088         request->GetName(name);
00089     else
00090         name.AssignLiteral("???");
00091 }
00092 #endif /* DEBUG */
00093 
00094 struct nsRequestInfo : public PLDHashEntryHdr
00095 {
00096   nsRequestInfo(const void *key)
00097     : mKey(key), mCurrentProgress(0), mMaxProgress(0), mUploading(PR_FALSE)
00098   {
00099   }
00100 
00101   const void* mKey; // Must be first for the pldhash stubs to work
00102   nsInt64 mCurrentProgress;
00103   nsInt64 mMaxProgress;
00104   PRBool mUploading;
00105 };
00106 
00107 
00108 PR_STATIC_CALLBACK(PRBool)
00109 RequestInfoHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
00110                          const void *key)
00111 {
00112   // Initialize the entry with placement new
00113   new (entry) nsRequestInfo(key);
00114   return PR_TRUE;
00115 }
00116 
00117 
00118 struct nsListenerInfo {
00119   nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask) 
00120     : mWeakListener(aListener),
00121       mNotifyMask(aNotifyMask)
00122   {
00123   }
00124 
00125   // Weak pointer for the nsIWebProgressListener...
00126   nsWeakPtr mWeakListener;
00127 
00128   // Mask indicating which notifications the listener wants to receive.
00129   unsigned long mNotifyMask;
00130 };
00131 
00132 
00133 nsDocLoader::nsDocLoader()
00134   : mListenerInfoList(8)
00135 {
00136 #if defined(PR_LOGGING)
00137   if (nsnull == gDocLoaderLog) {
00138       gDocLoaderLog = PR_NewLogModule("DocLoader");
00139   }
00140 #endif /* PR_LOGGING */
00141 
00142   mParent    = nsnull;
00143 
00144   mIsLoadingDocument = PR_FALSE;
00145   mIsRestoringDocument = PR_FALSE;
00146 
00147   static PLDHashTableOps hash_table_ops =
00148   {
00149     PL_DHashAllocTable,
00150     PL_DHashFreeTable,
00151     PL_DHashGetKeyStub,
00152     PL_DHashVoidPtrKeyStub,
00153     PL_DHashMatchEntryStub,
00154     PL_DHashMoveEntryStub,
00155     PL_DHashClearEntryStub,
00156     PL_DHashFinalizeStub,
00157     RequestInfoHashInitEntry
00158   };
00159 
00160   if (!PL_DHashTableInit(&mRequestInfoHash, &hash_table_ops, nsnull,
00161                          sizeof(nsRequestInfo), 16)) {
00162     mRequestInfoHash.ops = nsnull;
00163   }
00164 
00165   ClearInternalProgress();
00166 
00167   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00168          ("DocLoader:%p: created.\n", this));
00169 }
00170 
00171 nsresult
00172 nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
00173 {
00174   mParent = aParent;
00175   return NS_OK; 
00176 }
00177 
00178 nsresult
00179 nsDocLoader::Init()
00180 {
00181   if (!mRequestInfoHash.ops) {
00182     return NS_ERROR_OUT_OF_MEMORY;
00183   }
00184 
00185   nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
00186   if (NS_FAILED(rv)) return rv;
00187 
00188   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00189          ("DocLoader:%p: load group %x.\n", this, mLoadGroup.get()));
00190 
00191   return NS_OK;
00192 }
00193 
00194 nsDocLoader::~nsDocLoader()
00195 {
00196               /*
00197                      |ClearWeakReferences()| here is intended to prevent people holding weak references
00198                      from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
00199                      subsequent |Release()| will try to destroy me.  At this point there should be only
00200                      weak references remaining (otherwise, we wouldn't be getting destroyed).
00201 
00202                      An alternative would be incrementing our refcount (consider it a compressed flag
00203                      saying "Don't re-destroy.").  I haven't yet decided which is better. [scc]
00204               */
00205   // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
00206   // this needed?
00207   ClearWeakReferences();
00208 
00209   Destroy();
00210 
00211   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
00212          ("DocLoader:%p: deleted.\n", this));
00213 
00214   if (mRequestInfoHash.ops) {
00215     PL_DHashTableFinish(&mRequestInfoHash);
00216   }
00217 }
00218 
00219 
00220 /*
00221  * Implementation of ISupports methods...
00222  */
00223 NS_IMPL_THREADSAFE_ADDREF(nsDocLoader)
00224 NS_IMPL_THREADSAFE_RELEASE(nsDocLoader)
00225 
00226 NS_INTERFACE_MAP_BEGIN(nsDocLoader)
00227    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
00228    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00229    NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
00230    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00231    NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
00232    NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)   
00233    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
00234    NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
00235    NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
00236    NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
00237    if (aIID.Equals(kThisImplCID))
00238      foundInterface = NS_STATIC_CAST(nsIDocumentLoader *, this);
00239    else
00240 NS_INTERFACE_MAP_END
00241 
00242 
00243 /*
00244  * Implementation of nsIInterfaceRequestor methods...
00245  */
00246 NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
00247 {
00248   nsresult rv = NS_ERROR_NO_INTERFACE;
00249 
00250   NS_ENSURE_ARG_POINTER(aSink);
00251 
00252   if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
00253     *aSink = mLoadGroup;
00254     NS_IF_ADDREF((nsISupports*)*aSink);
00255     rv = NS_OK;
00256   } else {
00257     rv = QueryInterface(aIID, aSink);
00258   }
00259 
00260   return rv;
00261 }
00262 
00263 /* static */
00264 already_AddRefed<nsDocLoader>
00265 nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
00266 {
00267   if (!aSupports) {
00268     return nsnull;
00269   }
00270   
00271   nsDocLoader* ptr;
00272   CallQueryInterface(aSupports, &ptr);
00273   return ptr;
00274 }
00275 
00276 /* static */
00277 nsresult
00278 nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
00279 {
00280   nsresult rv;
00281   nsCOMPtr<nsIDocumentLoader> docLoaderService =
00282     do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
00283   NS_ENSURE_SUCCESS(rv, rv);
00284 
00285   nsRefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
00286   NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
00287 
00288   return rootDocLoader->AddChildLoader(aDocLoader);
00289 }
00290 
00291 NS_IMETHODIMP
00292 nsDocLoader::Stop(void)
00293 {
00294   nsresult rv = NS_OK;
00295   PRInt32 count, i;
00296 
00297   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00298          ("DocLoader:%p: Stop() called\n", this));
00299 
00300   count = mChildList.Count();
00301 
00302   nsCOMPtr<nsIDocumentLoader> loader;
00303   for (i=0; i < count; i++) {
00304     loader = ChildAt(i);
00305 
00306     if (loader) {
00307       (void) loader->Stop();
00308     }
00309   }
00310 
00311   if (mLoadGroup)
00312     rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
00313 
00314   // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
00315   // etc, as needed.  We could be getting into here from a subframe onload, in
00316   // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
00317   // happened yet, Canceling the loadgroup did nothing (because it was already
00318   // empty), and we're about to start a new load (which is what triggered this
00319   // Stop() call).
00320 
00321   // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
00322   // we wouldn't need the call here....
00323   DocLoaderIsEmpty();
00324   
00325   return rv;
00326 }       
00327 
00328 
00329 PRBool
00330 nsDocLoader::IsBusy()
00331 {
00332   nsresult rv;
00333 
00334   //
00335   // A document loader is busy if either:
00336   //
00337   //   1. It is currently loading a document (ie. one or more URIs)
00338   //   2. One of it's child document loaders is busy...
00339   //
00340 
00341   /* Is this document loader busy? */
00342   if (mIsLoadingDocument) {
00343     PRBool busy;
00344     rv = mLoadGroup->IsPending(&busy);
00345     if (NS_FAILED(rv))
00346       return PR_FALSE;
00347     if (busy)
00348       return PR_TRUE;
00349   }
00350 
00351   /* Otherwise, check its child document loaders... */
00352   PRInt32 count, i;
00353 
00354   count = mChildList.Count();
00355 
00356   for (i=0; i < count; i++) {
00357     nsIDocumentLoader* loader = ChildAt(i);
00358 
00359     // This is a safe cast, because we only put nsDocLoader objects into the
00360     // array
00361     if (loader && NS_STATIC_CAST(nsDocLoader*, loader)->IsBusy())
00362         return PR_TRUE;
00363   }
00364 
00365   return PR_FALSE;
00366 }
00367 
00368 NS_IMETHODIMP
00369 nsDocLoader::GetContainer(nsISupports** aResult)
00370 {
00371    NS_ADDREF(*aResult = NS_STATIC_CAST(nsIDocumentLoader*, this));
00372 
00373    return NS_OK;
00374 }
00375 
00376 NS_IMETHODIMP
00377 nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
00378 {
00379   nsresult rv = NS_OK;
00380 
00381   if (nsnull == aResult) {
00382     rv = NS_ERROR_NULL_POINTER;
00383   } else {
00384     *aResult = mLoadGroup;
00385     NS_IF_ADDREF(*aResult);
00386   }
00387   return rv;
00388 }
00389 
00390 void
00391 nsDocLoader::Destroy()
00392 {
00393   Stop();
00394 
00395   // Remove the document loader from the parent list of loaders...
00396   if (mParent) 
00397   {
00398     mParent->RemoveChildLoader(this);
00399   }
00400 
00401   // Release all the information about network requests...
00402   ClearRequestInfoHash();
00403 
00404   // Release all the information about registered listeners...
00405   PRInt32 count = mListenerInfoList.Count();
00406   for(PRInt32 i = 0; i < count; i++) {
00407     nsListenerInfo *info =
00408       NS_STATIC_CAST(nsListenerInfo*, mListenerInfoList.ElementAt(i));
00409 
00410     delete info;
00411   }
00412 
00413   mListenerInfoList.Clear();
00414   mListenerInfoList.Compact();
00415 
00416   mDocumentRequest = 0;
00417 
00418   if (mLoadGroup)
00419     mLoadGroup->SetGroupObserver(nsnull);
00420 
00421   DestroyChildren();
00422 }
00423 
00424 void
00425 nsDocLoader::DestroyChildren()
00426 {
00427   PRInt32 i, count;
00428   
00429   count = mChildList.Count();
00430   // if the doc loader still has children...we need to enumerate the
00431   // children and make them null out their back ptr to the parent doc
00432   // loader
00433   for (i=0; i < count; i++)
00434   {
00435     nsIDocumentLoader* loader = ChildAt(i);
00436 
00437     if (loader) {
00438       // This is a safe cast, as we only put nsDocLoader objects into the
00439       // array
00440       NS_STATIC_CAST(nsDocLoader*, loader)->SetDocLoaderParent(nsnull);
00441     }
00442   }
00443   mChildList.Clear();
00444 }
00445 
00446 NS_IMETHODIMP
00447 nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
00448 {
00449   // called each time a request is added to the group.
00450 
00451 #ifdef PR_LOGGING
00452   if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
00453     nsCAutoString name;
00454     request->GetName(name);
00455 
00456     PRUint32 count = 0;
00457     if (mLoadGroup)
00458       mLoadGroup->GetActiveCount(&count);
00459 
00460     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
00461            ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
00462             this, request, name.get(),
00463             (mIsLoadingDocument ? "true" : "false"),
00464             count));
00465   }
00466 #endif /* PR_LOGGING */
00467   PRBool bJustStartedLoading = PR_FALSE;
00468 
00469   nsLoadFlags loadFlags = 0;
00470   request->GetLoadFlags(&loadFlags);
00471 
00472   if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
00473       bJustStartedLoading = PR_TRUE;
00474       mIsLoadingDocument = PR_TRUE;
00475       ClearInternalProgress(); // only clear our progress if we are starting a new load....
00476   }
00477 
00478   //
00479   // Only fire an OnStartDocumentLoad(...) if the document loader
00480   // has initiated a load...  Otherwise, this notification has
00481   // resulted from a request being added to the load group.
00482   //
00483   if (mIsLoadingDocument) {
00484     //
00485     // Create a new nsRequestInfo for the request that is starting to
00486     // load...
00487     //
00488     AddRequestInfo(request);
00489 
00490     if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
00491       //
00492       // Make sure that the document channel is null at this point...
00493       // (unless its been redirected)
00494       //
00495       NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
00496                    !(mDocumentRequest.get()),
00497                    "Overwriting an existing document channel!");
00498 
00499       // This request is associated with the entire document...
00500       mDocumentRequest = request;
00501       mLoadGroup->SetDefaultLoadRequest(request); 
00502 
00503       // Only fire the start document load notification for the first
00504       // document URI...  Do not fire it again for redirections
00505       //
00506       if (bJustStartedLoading) {
00507         // Update the progress status state
00508         mProgressStateFlags = nsIWebProgressListener::STATE_START;
00509 
00510         // Fire the start document load notification
00511         doStartDocumentLoad();
00512         return NS_OK;
00513       }
00514     } 
00515   }
00516   else {
00517     // The DocLoader is not busy, so clear out any cached information...
00518     ClearRequestInfoHash();
00519   }
00520 
00521   NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
00522                "mDocumentRequest MUST be set for the duration of a page load!");
00523 
00524   doStartURLLoad(request);
00525 
00526   return NS_OK;
00527 }
00528 
00529 NS_IMETHODIMP
00530 nsDocLoader::OnStopRequest(nsIRequest *aRequest, 
00531                                nsISupports *aCtxt, 
00532                                nsresult aStatus)
00533 {
00534   nsresult rv = NS_OK;
00535 
00536 #ifdef PR_LOGGING
00537   if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
00538     nsCAutoString name;
00539     aRequest->GetName(name);
00540 
00541     PRUint32 count = 0;
00542     if (mLoadGroup)
00543       mLoadGroup->GetActiveCount(&count);
00544 
00545     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
00546            ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
00547            this, aRequest, name.get(),
00548            aStatus, (mIsLoadingDocument ? "true" : "false"),
00549            count));
00550   }
00551 #endif
00552 
00553   //
00554   // Only fire the OnEndDocumentLoad(...) if the document loader 
00555   // has initiated a load...
00556   //
00557   if (mIsLoadingDocument) {
00558     PRUint32 count;
00559     PRBool bFireTransferring = PR_FALSE;
00560 
00561     //
00562     // Set the Maximum progress to the same value as the current progress.
00563     // Since the URI has finished loading, all the data is there.  Also,
00564     // this will allow a more accurate estimation of the max progress (in case
00565     // the old value was unknown ie. -1)
00566     //
00567     nsRequestInfo *info = GetRequestInfo(aRequest);
00568     if (info) {
00569       nsInt64 oldMax = info->mMaxProgress;
00570 
00571       info->mMaxProgress = info->mCurrentProgress;
00572       //
00573       // If a request whose content-length was previously unknown has just
00574       // finished loading, then use this new data to try to calculate a
00575       // mMaxSelfProgress...
00576       //
00577       if ((oldMax < nsInt64(0)) && (mMaxSelfProgress < nsInt64(0))) {
00578         mMaxSelfProgress = CalculateMaxProgress();
00579       }
00580 
00581       //
00582       // Determine whether a STATE_TRANSFERRING notification should be 
00583       // 'synthesized'.
00584       //
00585       // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
00586       // nsRequestInfo::mCurrentProgress are both 0, then the
00587       // STATE_TRANSFERRING notification has not been fired yet...
00588       //
00589       if ((oldMax == LL_ZERO) && (info->mCurrentProgress == LL_ZERO)) {
00590         nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
00591 
00592         // Only fire a TRANSFERRING notification if the request is also a
00593         // channel -- data transfer requires a nsIChannel!
00594         //
00595         if (channel) {
00596           if (NS_SUCCEEDED(aStatus)) {
00597             bFireTransferring = PR_TRUE;
00598           } 
00599           //
00600           // If the request failed (for any reason other than being
00601           // redirected or retargeted), the TRANSFERRING notification can
00602           // still be fired if a HTTP connection was established to a server.
00603           //
00604           else if (aStatus != NS_BINDING_REDIRECTED &&
00605                    aStatus != NS_BINDING_RETARGETED) {
00606             //
00607             // Only if the load has been targeted (see bug 268483)...
00608             //
00609             PRUint32 lf;
00610             channel->GetLoadFlags(&lf);
00611             if (lf & nsIChannel::LOAD_TARGETED) {
00612               nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
00613               if (httpChannel) {
00614                 PRUint32 responseCode;
00615                 rv = httpChannel->GetResponseStatus(&responseCode);
00616                 if (NS_SUCCEEDED(rv)) {
00617                   //
00618                   // A valid server status indicates that a connection was
00619                   // established to the server... So, fire the notification
00620                   // even though a failure occurred later...
00621                   //
00622                   bFireTransferring = PR_TRUE;
00623                 }
00624               }
00625             }
00626           }
00627         }
00628       }
00629     }
00630 
00631     if (bFireTransferring) {
00632       // Send a STATE_TRANSFERRING notification for the request.
00633       PRInt32 flags;
00634     
00635       flags = nsIWebProgressListener::STATE_TRANSFERRING | 
00636               nsIWebProgressListener::STATE_IS_REQUEST;
00637       //
00638       // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
00639       //
00640       if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
00641         mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
00642 
00643         // Send STATE_TRANSFERRING for the document too...
00644         flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
00645       }
00646 
00647       FireOnStateChange(this, aRequest, flags, NS_OK);
00648     }
00649 
00650     //
00651     // Fire the OnStateChange(...) notification for stop request
00652     //
00653     doStopURLLoad(aRequest, aStatus);
00654     
00655     rv = mLoadGroup->GetActiveCount(&count);
00656     if (NS_FAILED(rv)) return rv;
00657 
00658     //
00659     // The load group for this DocumentLoader is idle...
00660     //
00661     if (0 == count) {
00662       DocLoaderIsEmpty();
00663     }
00664   }
00665   else {
00666     doStopURLLoad(aRequest, aStatus); 
00667   }
00668   
00669   return NS_OK;
00670 }
00671 
00672 
00673 nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
00674 {
00675   nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
00676   if (NS_SUCCEEDED(rv)) {
00677     aChild->SetDocLoaderParent(nsnull);
00678   }
00679   return rv;
00680 }
00681 
00682 nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
00683 {
00684   nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00685   if (NS_SUCCEEDED(rv)) {
00686     aChild->SetDocLoaderParent(this);
00687   }
00688   return rv;
00689 }
00690 
00691 NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
00692 {
00693   if (!mDocumentRequest) {
00694     *aChannel = nsnull;
00695     return NS_OK;
00696   }
00697   
00698   return CallQueryInterface(mDocumentRequest, aChannel);
00699 }
00700 
00701 
00702 void nsDocLoader::DocLoaderIsEmpty()
00703 {
00704   if (mIsLoadingDocument) {
00705     /* In the unimagineably rude circumstance that onload event handlers
00706        triggered by this function actually kill the window ... ok, it's
00707        not unimagineable; it's happened ... this deathgrip keeps this object
00708        alive long enough to survive this function call. */
00709     nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
00710 
00711     if (!IsBusy()) {
00712       PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00713              ("DocLoader:%p: Is now idle...\n", this));
00714 
00715       nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
00716 
00717       NS_ASSERTION(mDocumentRequest, "No Document Request!");
00718       mDocumentRequest = 0;
00719       mIsLoadingDocument = PR_FALSE;
00720 
00721       // Update the progress status state - the document is done
00722       mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
00723 
00724 
00725       nsresult loadGroupStatus = NS_OK; 
00726       mLoadGroup->GetStatus(&loadGroupStatus);
00727 
00728       // 
00729       // New code to break the circular reference between 
00730       // the load group and the docloader... 
00731       // 
00732       mLoadGroup->SetDefaultLoadRequest(nsnull); 
00733 
00734       //
00735       // Do nothing after firing the OnEndDocumentLoad(...). The document
00736       // loader may be loading a *new* document - if LoadDocument()
00737       // was called from a handler!
00738       //
00739       doStopDocumentLoad(docRequest, loadGroupStatus);
00740 
00741       if (mParent) {
00742         mParent->DocLoaderIsEmpty();
00743       }
00744     }
00745   }
00746 }
00747 
00748 void nsDocLoader::doStartDocumentLoad(void)
00749 {
00750 
00751 #if defined(DEBUG)
00752   nsCAutoString buffer;
00753 
00754   GetURIStringFromRequest(mDocumentRequest, buffer);
00755   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00756          ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
00757           "\tURI: %s \n",
00758           this, buffer.get()));
00759 #endif /* DEBUG */
00760 
00761   // Fire an OnStatus(...) notification STATE_START.  This indicates
00762   // that the document represented by mDocumentRequest has started to
00763   // load...
00764   FireOnStateChange(this,
00765                     mDocumentRequest,
00766                     nsIWebProgressListener::STATE_START |
00767                     nsIWebProgressListener::STATE_IS_DOCUMENT |
00768                     nsIWebProgressListener::STATE_IS_REQUEST |
00769                     nsIWebProgressListener::STATE_IS_WINDOW |
00770                     nsIWebProgressListener::STATE_IS_NETWORK,
00771                     NS_OK);
00772 }
00773 
00774 void nsDocLoader::doStartURLLoad(nsIRequest *request)
00775 {
00776 #if defined(DEBUG)
00777   nsCAutoString buffer;
00778 
00779   GetURIStringFromRequest(request, buffer);
00780     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00781           ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
00782            "\tURI: %s\n",
00783             this, buffer.get()));
00784 #endif /* DEBUG */
00785 
00786   FireOnStateChange(this,
00787                     request,
00788                     nsIWebProgressListener::STATE_START |
00789                     nsIWebProgressListener::STATE_IS_REQUEST,
00790                     NS_OK);
00791 }
00792 
00793 void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
00794 {
00795 #if defined(DEBUG)
00796   nsCAutoString buffer;
00797 
00798   GetURIStringFromRequest(request, buffer);
00799     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00800           ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
00801            "\tURI: %s status=%x\n",
00802             this, buffer.get(), aStatus));
00803 #endif /* DEBUG */
00804 
00805   FireOnStateChange(this,
00806                     request,
00807                     nsIWebProgressListener::STATE_STOP |
00808                     nsIWebProgressListener::STATE_IS_REQUEST,
00809                     aStatus);
00810 }
00811 
00812 void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
00813                                          nsresult aStatus)
00814 {
00815 #if defined(DEBUG)
00816   nsCAutoString buffer;
00817 
00818   GetURIStringFromRequest(request, buffer);
00819   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00820          ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
00821          "\tURI: %s Status=%x\n",
00822           this, buffer.get(), aStatus));
00823 #endif /* DEBUG */
00824 
00825   //
00826   // Fire an OnStateChange(...) notification indicating the the
00827   // current document has finished loading...
00828   //
00829   FireOnStateChange(this,
00830                     request,
00831                     nsIWebProgressListener::STATE_STOP |
00832                     nsIWebProgressListener::STATE_IS_DOCUMENT,
00833                     aStatus);
00834 
00835   //
00836   // Fire a final OnStateChange(...) notification indicating the the
00837   // current document has finished loading...
00838   //
00839   FireOnStateChange(this,
00840                     request,
00841                     nsIWebProgressListener::STATE_STOP |
00842                     nsIWebProgressListener::STATE_IS_WINDOW |
00843                     nsIWebProgressListener::STATE_IS_NETWORK,
00844                     aStatus);
00845 }
00846 
00848 // The following section contains support for nsIWebProgress and related stuff
00850 
00851 NS_IMETHODIMP
00852 nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
00853                                      PRUint32 aNotifyMask)
00854 {
00855   nsresult rv;
00856 
00857   nsListenerInfo* info = GetListenerInfo(aListener);
00858   if (info) {
00859     // The listener is already registered!
00860     return NS_ERROR_FAILURE;
00861   }
00862 
00863   nsWeakPtr listener = do_GetWeakReference(aListener);
00864   if (!listener) {
00865     return NS_ERROR_INVALID_ARG;
00866   }
00867 
00868   info = new nsListenerInfo(listener, aNotifyMask);
00869   if (!info) {
00870     return NS_ERROR_OUT_OF_MEMORY;
00871   }
00872 
00873   rv = mListenerInfoList.AppendElement(info) ? NS_OK : NS_ERROR_FAILURE;
00874   return rv;
00875 }
00876 
00877 NS_IMETHODIMP
00878 nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
00879 {
00880   nsresult rv;
00881 
00882   nsListenerInfo* info = GetListenerInfo(aListener);
00883   if (info) {
00884     rv = mListenerInfoList.RemoveElement(info) ? NS_OK : NS_ERROR_FAILURE;
00885     delete info;
00886   } else {
00887     // The listener is not registered!
00888     rv = NS_ERROR_FAILURE;
00889   }
00890   return rv;
00891 }
00892 
00893 NS_IMETHODIMP
00894 nsDocLoader::GetDOMWindow(nsIDOMWindow **aResult)
00895 {
00896   return CallGetInterface(this, aResult);
00897 }
00898 
00899 NS_IMETHODIMP
00900 nsDocLoader::GetIsLoadingDocument(PRBool *aIsLoadingDocument)
00901 {
00902   *aIsLoadingDocument = mIsLoadingDocument;
00903 
00904   return NS_OK;
00905 }
00906 
00907 PRInt64 nsDocLoader::GetMaxTotalProgress()
00908 {
00909   nsInt64 newMaxTotal = 0;
00910 
00911   PRInt32 count = mChildList.Count();
00912   nsCOMPtr<nsIWebProgress> webProgress;
00913   for (PRInt32 i=0; i < count; i++) 
00914   {
00915     nsInt64 individualProgress = 0;
00916     nsIDocumentLoader* docloader = ChildAt(i);
00917     if (docloader)
00918     {
00919       // Cast is safe since all children are nsDocLoader too
00920       individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
00921     }
00922     if (individualProgress < nsInt64(0)) // if one of the elements doesn't know it's size
00923                                          // then none of them do
00924     {
00925        newMaxTotal = nsInt64(-1);
00926        break;
00927     }
00928     else
00929      newMaxTotal += individualProgress;
00930   }
00931 
00932   nsInt64 progress = -1;
00933   if (mMaxSelfProgress >= nsInt64(0) && newMaxTotal >= nsInt64(0))
00934     progress = newMaxTotal + mMaxSelfProgress;
00935   
00936   return progress;
00937 }
00938 
00940 // The following section contains support for nsIProgressEventSink which is used to 
00941 // pass progress and status between the actual request and the doc loader. The doc loader
00942 // then turns around and makes the right web progress calls based on this information.
00944 
00945 NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt, 
00946                                       PRUint64 aProgress, PRUint64 aProgressMax)
00947 {
00948   nsRequestInfo *info;
00949   nsInt64 progressDelta = 0;
00950 
00951   //
00952   // Update the RequestInfo entry with the new progress data
00953   //
00954   info = GetRequestInfo(aRequest);
00955   if (info) {
00956     // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
00957     if (!info->mUploading && (nsInt64(0) == info->mCurrentProgress) && (nsInt64(0) == info->mMaxProgress)) {
00958       //
00959       // If we receive an OnProgress event from a toplevel channel that the URI Loader
00960       // has not yet targeted, then we must suppress the event.  This is necessary to
00961       // ensure that webprogresslisteners do not get confused when the channel is
00962       // finally targeted.  See bug 257308.
00963       //
00964       nsLoadFlags lf = 0;
00965       aRequest->GetLoadFlags(&lf);
00966       if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
00967         PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
00968             ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
00969         return NS_OK;
00970       }
00971 
00972       //
00973       // This is the first progress notification for the entry.  If
00974       // (aMaxProgress > 0) then the content-length of the data is known,
00975       // so update mMaxSelfProgress...  Otherwise, set it to -1 to indicate
00976       // that the content-length is no longer known.
00977       //
00978       if (nsUint64(aProgressMax) != LL_MAXUINT) {
00979         mMaxSelfProgress  += PRInt64(aProgressMax);
00980         info->mMaxProgress = PRInt64(aProgressMax);
00981       } else {
00982         mMaxSelfProgress   =  nsInt64(-1);
00983         info->mMaxProgress =  nsInt64(-1);
00984       }
00985 
00986       // Send a STATE_TRANSFERRING notification for the request.
00987       PRInt32 flags;
00988     
00989       flags = nsIWebProgressListener::STATE_TRANSFERRING | 
00990               nsIWebProgressListener::STATE_IS_REQUEST;
00991       //
00992       // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
00993       //
00994       if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
00995         mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
00996 
00997         // Send STATE_TRANSFERRING for the document too...
00998         flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
00999       }
01000 
01001       FireOnStateChange(this, aRequest, flags, NS_OK);
01002     }
01003 
01004     // Update the current progress count...
01005     progressDelta = nsInt64(PRInt64(aProgress)) - info->mCurrentProgress;
01006     mCurrentSelfProgress += progressDelta;
01007 
01008     info->mCurrentProgress = PRInt64(aProgress);
01009   } 
01010   //
01011   // The request is not part of the load group, so ignore its progress
01012   // information...
01013   //
01014   else {
01015 #if defined(DEBUG)
01016     nsCAutoString buffer;
01017 
01018     GetURIStringFromRequest(aRequest, buffer);
01019     PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
01020            ("DocLoader:%p OOPS - No Request Info for: %s\n",
01021             this, buffer.get()));
01022 #endif /* DEBUG */
01023 
01024     return NS_OK;
01025   }
01026 
01027   //
01028   // Fire progress notifications out to any registered nsIWebProgressListeners
01029   //
01030   FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
01031                        mCurrentTotalProgress, mMaxTotalProgress);
01032 
01033   return NS_OK;
01034 }
01035 
01036 NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt, 
01037                                         nsresult aStatus, const PRUnichar* aStatusArg)
01038 {
01039   //
01040   // Fire progress notifications out to any registered nsIWebProgressListeners
01041   //
01042   if (aStatus) {
01043     // Remember the current status for this request
01044     nsRequestInfo *info;
01045     info = GetRequestInfo(aRequest);
01046     if (info) {
01047       PRBool uploading = (aStatus == nsITransport::STATUS_WRITING ||
01048                           aStatus == nsISocketTransport::STATUS_SENDING_TO);
01049       // If switching from uploading to downloading (or vice versa), then we
01050       // need to reset our progress counts.  This is designed with HTTP form
01051       // submission in mind, where an upload is performed followed by download
01052       // of possibly several documents.
01053       if (info->mUploading != uploading) {
01054         mCurrentSelfProgress  = mMaxSelfProgress  = LL_ZERO;
01055         mCurrentTotalProgress = mMaxTotalProgress = LL_ZERO;
01056         info->mUploading = uploading;
01057         info->mCurrentProgress = LL_ZERO;
01058         info->mMaxProgress = LL_ZERO;
01059       }
01060     }
01061     
01062     nsresult rv;
01063     nsCOMPtr<nsIStringBundleService> sbs = do_GetService(kStringBundleServiceCID, &rv);
01064     if (NS_FAILED(rv)) return rv;
01065     nsXPIDLString msg;
01066     rv = sbs->FormatStatusMessage(aStatus, aStatusArg, getter_Copies(msg));
01067     if (NS_FAILED(rv)) return rv;
01068     FireOnStatusChange(this, aRequest, aStatus, msg);
01069   }
01070   return NS_OK;
01071 }
01072 
01073 void nsDocLoader::ClearInternalProgress()
01074 {
01075   ClearRequestInfoHash();
01076 
01077   mCurrentSelfProgress  = mMaxSelfProgress  = LL_ZERO;
01078   mCurrentTotalProgress = mMaxTotalProgress = LL_ZERO;
01079 
01080   mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
01081 }
01082 
01083 
01084 void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
01085                                        nsIRequest *request,
01086                                        PRInt64 aProgress,
01087                                        PRInt64 aProgressMax,
01088                                        PRInt64 aProgressDelta,
01089                                        PRInt64 aTotalProgress,
01090                                        PRInt64 aMaxTotalProgress)
01091 {
01092   if (mIsLoadingDocument) {
01093     mCurrentTotalProgress += aProgressDelta;
01094     mMaxTotalProgress = GetMaxTotalProgress();
01095 
01096     aTotalProgress    = mCurrentTotalProgress;
01097     aMaxTotalProgress = mMaxTotalProgress;
01098   }
01099 
01100 #if defined(DEBUG)
01101   nsCAutoString buffer;
01102 
01103   GetURIStringFromRequest(request, buffer);
01104   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
01105          ("DocLoader:%p: Progress (%s): curSelf: %d maxSelf: %d curTotal: %d maxTotal %d\n",
01106           this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
01107 #endif /* DEBUG */
01108 
01109   /*
01110    * First notify any listeners of the new progress info...
01111    *
01112    * Operate the elements from back to front so that if items get
01113    * get removed from the list it won't affect our iteration
01114    */
01115   nsCOMPtr<nsIWebProgressListener> listener;
01116   PRInt32 count = mListenerInfoList.Count();
01117 
01118   while (--count >= 0) {
01119     nsListenerInfo *info;
01120 
01121     info = NS_STATIC_CAST(nsListenerInfo*,mListenerInfoList.SafeElementAt(count));
01122     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_PROGRESS)) {
01123       continue;
01124     }
01125 
01126     listener = do_QueryReferent(info->mWeakListener);
01127     if (!listener) {
01128       // the listener went away. gracefully pull it out of the list.
01129       mListenerInfoList.RemoveElementAt(count);
01130       delete info;
01131       continue;
01132     }
01133 
01134     // XXX truncates 64-bit to 32-bit
01135     listener->OnProgressChange(aLoadInitiator,request,
01136                                PRInt32(aProgress), PRInt32(aProgressMax),
01137                                PRInt32(aTotalProgress), PRInt32(aMaxTotalProgress));
01138   }
01139 
01140   mListenerInfoList.Compact();
01141 
01142   // Pass the notification up to the parent...
01143   if (mParent) {
01144     mParent->FireOnProgressChange(aLoadInitiator, request,
01145                                   aProgress, aProgressMax,
01146                                   aProgressDelta,
01147                                   aTotalProgress, aMaxTotalProgress);
01148   }
01149 }
01150 
01151 
01152 void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
01153                                     nsIRequest *aRequest,
01154                                     PRInt32 aStateFlags,
01155                                     nsresult aStatus)
01156 {
01157   //
01158   // Remove the STATE_IS_NETWORK bit if necessary.
01159   //
01160   // The rule is to remove this bit, if the notification has been passed
01161   // up from a child WebProgress, and the current WebProgress is already
01162   // active...
01163   //
01164   if (mIsLoadingDocument &&
01165       (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) && 
01166       (this != aProgress)) {
01167     aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
01168   }
01169 
01170   // Add the STATE_RESTORING bit if necessary.
01171   if (mIsRestoringDocument)
01172     aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
01173 
01174 #if defined(DEBUG)
01175   nsCAutoString buffer;
01176 
01177   GetURIStringFromRequest(aRequest, buffer);
01178   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
01179          ("DocLoader:%p: Status (%s): code: %x\n",
01180          this, buffer.get(), aStateFlags));
01181 #endif /* DEBUG */
01182 
01183   NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
01184 
01185   /*                                                                           
01186    * First notify any listeners of the new state info...
01187    *
01188    * Operate the elements from back to front so that if items get
01189    * get removed from the list it won't affect our iteration
01190    */
01191   nsCOMPtr<nsIWebProgressListener> listener;
01192   PRInt32 count = mListenerInfoList.Count();
01193 
01194   while (--count >= 0) {
01195     nsListenerInfo *info;
01196 
01197     info = NS_STATIC_CAST(nsListenerInfo*,mListenerInfoList.SafeElementAt(count));
01198     if (!info || !(info->mNotifyMask & (aStateFlags >>16))) {
01199       continue;
01200     }
01201 
01202     listener = do_QueryReferent(info->mWeakListener);
01203     if (!listener) {
01204       // the listener went away. gracefully pull it out of the list.
01205       mListenerInfoList.RemoveElementAt(count);
01206       delete info;
01207       continue;
01208     }
01209 
01210     listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
01211   }
01212 
01213   mListenerInfoList.Compact();
01214 
01215   // Pass the notification up to the parent...
01216   if (mParent) {
01217     mParent->FireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
01218   }
01219 }
01220 
01221 
01222 
01223 void
01224 nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
01225                                   nsIRequest* aRequest,
01226                                   nsIURI *aUri)
01227 {
01228   /*                                                                           
01229    * First notify any listeners of the new state info...
01230    *
01231    * Operate the elements from back to front so that if items get
01232    * get removed from the list it won't affect our iteration
01233    */
01234   nsCOMPtr<nsIWebProgressListener> listener;
01235   PRInt32 count = mListenerInfoList.Count();
01236 
01237   while (--count >= 0) {
01238     nsListenerInfo *info;
01239 
01240     info = NS_STATIC_CAST(nsListenerInfo*,mListenerInfoList.SafeElementAt(count));
01241     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_LOCATION)) {
01242       continue;
01243     }
01244 
01245     listener = do_QueryReferent(info->mWeakListener);
01246     if (!listener) {
01247       // the listener went away. gracefully pull it out of the list.
01248       mListenerInfoList.RemoveElementAt(count);
01249       delete info;
01250       continue;
01251     }
01252 
01253     listener->OnLocationChange(aWebProgress, aRequest, aUri);
01254   }
01255 
01256   mListenerInfoList.Compact();
01257 
01258   // Pass the notification up to the parent...
01259   if (mParent) {
01260     mParent->FireOnLocationChange(aWebProgress, aRequest, aUri);
01261   }
01262 }
01263 
01264 void
01265 nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
01266                                 nsIRequest* aRequest,
01267                                 nsresult aStatus,
01268                                 const PRUnichar* aMessage)
01269 {
01270   /*                                                                           
01271    * First notify any listeners of the new state info...
01272    *
01273    * Operate the elements from back to front so that if items get
01274    * get removed from the list it won't affect our iteration
01275    */
01276   nsCOMPtr<nsIWebProgressListener> listener;
01277   PRInt32 count = mListenerInfoList.Count();
01278 
01279   while (--count >= 0) {
01280     nsListenerInfo *info;
01281 
01282     info = NS_STATIC_CAST(nsListenerInfo*,mListenerInfoList.SafeElementAt(count));
01283     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_STATUS)) {
01284       continue;
01285     }
01286 
01287     listener = do_QueryReferent(info->mWeakListener);
01288     if (!listener) {
01289       // the listener went away. gracefully pull it out of the list.
01290       mListenerInfoList.RemoveElementAt(count);
01291       delete info;
01292       continue;
01293     }
01294 
01295     listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
01296   }
01297   mListenerInfoList.Compact();
01298   
01299   // Pass the notification up to the parent...
01300   if (mParent) {
01301     mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
01302   }
01303 }
01304 
01305 nsListenerInfo * 
01306 nsDocLoader::GetListenerInfo(nsIWebProgressListener *aListener)
01307 {
01308   PRInt32 i, count;
01309   nsListenerInfo *info;
01310 
01311   nsCOMPtr<nsISupports> listener1 = do_QueryInterface(aListener);
01312   count = mListenerInfoList.Count();
01313   for (i=0; i<count; i++) {
01314     info = NS_STATIC_CAST(nsListenerInfo* ,mListenerInfoList.SafeElementAt(i));
01315 
01316     NS_ASSERTION(info, "There should NEVER be a null listener in the list");
01317     if (info) {
01318       nsCOMPtr<nsISupports> listener2 = do_QueryReferent(info->mWeakListener);
01319       if (listener1 == listener2)
01320         return info;
01321     }
01322   }
01323   return nsnull;
01324 }
01325 
01326 nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
01327 {
01328   if (!PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_ADD)) {
01329     return NS_ERROR_OUT_OF_MEMORY;
01330   }
01331 
01332   return NS_OK;
01333 }
01334 
01335 nsRequestInfo * nsDocLoader::GetRequestInfo(nsIRequest *aRequest)
01336 {
01337   nsRequestInfo *info =
01338     NS_STATIC_CAST(nsRequestInfo *,
01339                    PL_DHashTableOperate(&mRequestInfoHash, aRequest,
01340                                         PL_DHASH_LOOKUP));
01341 
01342   if (PL_DHASH_ENTRY_IS_FREE(info)) {
01343     // Nothing found in the hash, return null.
01344 
01345     return nsnull;
01346   }
01347 
01348   // Return what we found in the hash...
01349 
01350   return info;
01351 }
01352 
01353 // PLDHashTable enumeration callback that just removes every entry
01354 // from the hash.
01355 PR_STATIC_CALLBACK(PLDHashOperator)
01356 RemoveInfoCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number,
01357                    void *arg)
01358 {
01359   return PL_DHASH_REMOVE;
01360 }
01361 
01362 void nsDocLoader::ClearRequestInfoHash(void)
01363 {
01364   if (!mRequestInfoHash.ops || !mRequestInfoHash.entryCount) {
01365     // No hash, or the hash is empty, nothing to do here then...
01366 
01367     return;
01368   }
01369 
01370   PL_DHashTableEnumerate(&mRequestInfoHash, RemoveInfoCallback, nsnull);
01371 }
01372 
01373 // PLDHashTable enumeration callback that calculates the max progress.
01374 PR_STATIC_CALLBACK(PLDHashOperator)
01375 CalcMaxProgressCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
01376                         PRUint32 number, void *arg)
01377 {
01378   const nsRequestInfo *info = NS_STATIC_CAST(const nsRequestInfo *, hdr);
01379   nsInt64 *max = NS_STATIC_CAST(nsInt64 *, arg);
01380 
01381   if (info->mMaxProgress < info->mCurrentProgress) {
01382     *max = nsInt64(-1);
01383 
01384     return PL_DHASH_STOP;
01385   }
01386 
01387   *max += info->mMaxProgress;
01388 
01389   return PL_DHASH_NEXT;
01390 }
01391 
01392 PRInt64 nsDocLoader::CalculateMaxProgress()
01393 {
01394   nsInt64 max = 0;
01395   PL_DHashTableEnumerate(&mRequestInfoHash, CalcMaxProgressCallback, &max);
01396   return max;
01397 }
01398 
01399 NS_IMETHODIMP nsDocLoader::OnChannelRedirect(nsIChannel *aOldChannel,
01400                                              nsIChannel *aNewChannel,
01401                                              PRUint32    aFlags)
01402 {
01403   if (aOldChannel)
01404   {
01405     nsresult rv;
01406     nsCOMPtr<nsIURI> oldURI, newURI;
01407 
01408     rv = aOldChannel->GetOriginalURI(getter_AddRefs(oldURI));
01409     if (NS_FAILED(rv)) return rv;
01410 
01411     rv = aNewChannel->GetURI(getter_AddRefs(newURI));
01412     if (NS_FAILED(rv)) return rv;
01413 
01414     nsLoadFlags loadFlags = 0;
01415     PRInt32 stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
01416                          nsIWebProgressListener::STATE_IS_REQUEST;
01417 
01418     aOldChannel->GetLoadFlags(&loadFlags);
01419     // If the document channel is being redirected, then indicate that the
01420     // document is being redirected in the notification...
01421     if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
01422     {
01423       stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
01424 
01425 #if defined(DEBUG)
01426       nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
01427       NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
01428 #endif /* DEBUG */
01429     }
01430 
01431     OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
01432     FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
01433   }
01434 
01435   return NS_OK;
01436 }
01437 
01438 /*
01439  * Implementation of nsISecurityEventSink method...
01440  */
01441 
01442 NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
01443                                             PRUint32 aState)
01444 {
01445   //
01446   // Fire progress notifications out to any registered nsIWebProgressListeners.  
01447   //
01448   
01449   nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
01450   nsIWebProgress* webProgress = NS_STATIC_CAST(nsIWebProgress*, this);
01451 
01452   /*                                                                           
01453    * First notify any listeners of the new state info...
01454    *
01455    * Operate the elements from back to front so that if items get
01456    * get removed from the list it won't affect our iteration
01457    */
01458   nsCOMPtr<nsIWebProgressListener> listener;
01459   PRInt32 count = mListenerInfoList.Count();
01460 
01461   while (--count >= 0) {
01462     nsListenerInfo *info;
01463 
01464     info = NS_STATIC_CAST(nsListenerInfo*,mListenerInfoList.SafeElementAt(count));
01465     if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_SECURITY)) {
01466       continue;
01467     }
01468 
01469     listener = do_QueryReferent(info->mWeakListener);
01470     if (!listener) {
01471       // the listener went away. gracefully pull it out of the list.
01472       mListenerInfoList.RemoveElementAt(count);
01473       delete info;
01474       continue;
01475     }
01476 
01477     listener->OnSecurityChange(webProgress, request, aState);
01478   }
01479 
01480   mListenerInfoList.Compact();
01481 
01482   // Pass the notification up to the parent...
01483   if (mParent) {
01484     mParent->OnSecurityChange(aContext, aState);
01485   }
01486   return NS_OK;
01487 }
01488 
01489 /*
01490  * Implementation of nsISupportsPriority methods...
01491  *
01492  * The priority of the DocLoader _is_ the priority of its LoadGroup.
01493  *
01494  * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
01495  * go away. 
01496  */
01497 
01498 NS_IMETHODIMP nsDocLoader::GetPriority(PRInt32 *aPriority)
01499 {
01500   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
01501   if (p)
01502     return p->GetPriority(aPriority);
01503 
01504   *aPriority = 0;
01505   return NS_OK;
01506 }
01507 
01508 NS_IMETHODIMP nsDocLoader::SetPriority(PRInt32 aPriority)
01509 {
01510   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
01511          ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
01512 
01513   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
01514   if (p)
01515     p->SetPriority(aPriority);
01516 
01517   PRInt32 count = mChildList.Count();
01518 
01519   nsDocLoader *loader;
01520   for (PRInt32 i=0; i < count; i++) {
01521     loader = NS_STATIC_CAST(nsDocLoader*, ChildAt(i));
01522     if (loader) {
01523       loader->SetPriority(aPriority);
01524     }
01525   }
01526 
01527   return NS_OK;
01528 }
01529 
01530 NS_IMETHODIMP nsDocLoader::AdjustPriority(PRInt32 aDelta)
01531 {
01532   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
01533          ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
01534 
01535   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
01536   if (p)
01537     p->AdjustPriority(aDelta);
01538 
01539   PRInt32 count = mChildList.Count();
01540 
01541   nsDocLoader *loader;
01542   for (PRInt32 i=0; i < count; i++) {
01543     loader = NS_STATIC_CAST(nsDocLoader*, ChildAt(i));
01544     if (loader) {
01545       loader->AdjustPriority(aDelta);
01546     }
01547   }
01548 
01549   return NS_OK;
01550 }
01551 
01552 
01553 
01554 
01555 #if 0
01556 void nsDocLoader::DumpChannelInfo()
01557 {
01558   nsChannelInfo *info;
01559   PRInt32 i, count;
01560   PRInt32 current=0, max=0;
01561 
01562   
01563   printf("==== DocLoader=%x\n", this);
01564 
01565   count = mChannelInfoList.Count();
01566   for(i=0; i<count; i++) {
01567     info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
01568 
01569 #if defined(DEBUG)
01570     nsCAutoString buffer;
01571     nsresult rv = NS_OK;
01572     if (info->mURI) {
01573       rv = info->mURI->GetSpec(buffer);
01574     }
01575 
01576     printf("  [%d] current=%d  max=%d [%s]\n", i,
01577            info->mCurrentProgress, 
01578            info->mMaxProgress, buffer.get());
01579 #endif /* DEBUG */
01580 
01581     current += info->mCurrentProgress;
01582     if (max >= 0) {
01583       if (info->mMaxProgress < info->mCurrentProgress) {
01584         max = -1;
01585       } else {
01586         max += info->mMaxProgress;
01587       }
01588     }
01589   }
01590 
01591   printf("\nCurrent=%d   Total=%d\n====\n", current, max);
01592 }
01593 #endif /* 0 */
01594