Back to index

lightning-sunbird  0.9+nobinonly
nsLoadGroup.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim: set sw=4 ts=4 sts=4 et cin: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Darin Fisher <darin@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsLoadGroup.h"
00041 #include "nsISupportsArray.h"
00042 #include "nsEnumeratorUtils.h"
00043 #include "nsIServiceManager.h"
00044 #include "nsIEventQueueService.h"
00045 #include "nsCOMPtr.h"
00046 #include "nsIURI.h"
00047 #include "prlog.h"
00048 #include "nsCRT.h"
00049 #include "netCore.h"
00050 #include "nsXPIDLString.h"
00051 #include "nsReadableUtils.h"
00052 #include "nsString.h"
00053 #include "nsVoidArray.h"
00054 
00055 #if defined(PR_LOGGING)
00056 //
00057 // Log module for nsILoadGroup logging...
00058 //
00059 // To enable logging (see prlog.h for full details):
00060 //
00061 //    set NSPR_LOG_MODULES=LoadGroup:5
00062 //    set NSPR_LOG_FILE=nspr.log
00063 //
00064 // this enables PR_LOG_DEBUG level information and places all output in
00065 // the file nspr.log
00066 //
00067 PRLogModuleInfo* gLoadGroupLog = nsnull;
00068 #endif
00069 
00070 #define LOG(args) PR_LOG(gLoadGroupLog, PR_LOG_DEBUG, args)
00071 
00073 
00074 class RequestMapEntry : public PLDHashEntryHdr
00075 {
00076 public:
00077     RequestMapEntry(nsIRequest *aRequest) :
00078         mKey(aRequest)
00079     {
00080     }
00081 
00082     nsCOMPtr<nsIRequest> mKey;
00083 };
00084 
00085 PR_STATIC_CALLBACK(const void *)
00086 RequestHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
00087 {
00088     RequestMapEntry *e = NS_STATIC_CAST(RequestMapEntry *, entry);
00089 
00090     return e->mKey.get();
00091 }
00092 
00093 PR_STATIC_CALLBACK(PRBool)
00094 RequestHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
00095                       const void *key)
00096 {
00097     const RequestMapEntry *e =
00098         NS_STATIC_CAST(const RequestMapEntry *, entry);
00099     const nsIRequest *request = NS_STATIC_CAST(const nsIRequest *, key);
00100 
00101     return e->mKey == request;
00102 }
00103 
00104 PR_STATIC_CALLBACK(void)
00105 RequestHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
00106 {
00107     RequestMapEntry *e = NS_STATIC_CAST(RequestMapEntry *, entry);
00108 
00109     // An entry is being cleared, let the entry do its own cleanup.
00110     e->~RequestMapEntry();
00111 }
00112 
00113 PR_STATIC_CALLBACK(PRBool)
00114 RequestHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
00115                      const void *key)
00116 {
00117     const nsIRequest *const_request = NS_STATIC_CAST(const nsIRequest *, key);
00118     nsIRequest *request = NS_CONST_CAST(nsIRequest *, const_request);
00119 
00120     // Initialize the entry with placement new
00121     new (entry) RequestMapEntry(request);
00122     return PR_TRUE;
00123 }
00124 
00125 
00126 static void
00127 RescheduleRequest(nsIRequest *aRequest, PRInt32 delta)
00128 {
00129     nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aRequest);
00130     if (p)
00131         p->AdjustPriority(delta);
00132 }
00133 
00134 PR_STATIC_CALLBACK(PLDHashOperator)
00135 RescheduleRequests(PLDHashTable *table, PLDHashEntryHdr *hdr,
00136                    PRUint32 number, void *arg)
00137 {
00138     RequestMapEntry *e = NS_STATIC_CAST(RequestMapEntry *, hdr);
00139     PRInt32 *delta = NS_STATIC_CAST(PRInt32 *, arg);
00140 
00141     RescheduleRequest(e->mKey, *delta);
00142     return PL_DHASH_NEXT;
00143 }
00144 
00145 
00146 nsLoadGroup::nsLoadGroup(nsISupports* outer)
00147     : mForegroundCount(0)
00148     , mLoadFlags(LOAD_NORMAL)
00149     , mStatus(NS_OK)
00150     , mPriority(PRIORITY_NORMAL)
00151     , mIsCanceling(PR_FALSE)
00152 {
00153     NS_INIT_AGGREGATED(outer);
00154 
00155 #if defined(PR_LOGGING)
00156     // Initialize the global PRLogModule for nsILoadGroup logging
00157     if (nsnull == gLoadGroupLog)
00158         gLoadGroupLog = PR_NewLogModule("LoadGroup");
00159 #endif
00160 
00161     LOG(("LOADGROUP [%x]: Created.\n", this));
00162 
00163     // Initialize the ops in the hash to null to make sure we get
00164     // consistent errors if someone fails to call ::Init() on an
00165     // nsLoadGroup.
00166     mRequests.ops = nsnull;
00167 }
00168 
00169 nsLoadGroup::~nsLoadGroup()
00170 {
00171     nsresult rv;
00172 
00173     rv = Cancel(NS_BINDING_ABORTED);
00174     NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed");
00175 
00176     if (mRequests.ops) {
00177         PL_DHashTableFinish(&mRequests);
00178     }
00179 
00180     mDefaultLoadRequest = 0;
00181 
00182     LOG(("LOADGROUP [%x]: Destroyed.\n", this));
00183 }
00184 
00185 
00186 nsresult nsLoadGroup::Init()
00187 {
00188     static PLDHashTableOps hash_table_ops =
00189     {
00190         PL_DHashAllocTable,
00191         PL_DHashFreeTable,
00192         RequestHashGetKey,
00193         PL_DHashVoidPtrKeyStub,
00194         RequestHashMatchEntry,
00195         PL_DHashMoveEntryStub,
00196         RequestHashClearEntry,
00197         PL_DHashFinalizeStub,
00198         RequestHashInitEntry
00199     };
00200 
00201     if (!PL_DHashTableInit(&mRequests, &hash_table_ops, nsnull,
00202                            sizeof(RequestMapEntry), 16)) {
00203         mRequests.ops = nsnull;
00204 
00205         return NS_ERROR_OUT_OF_MEMORY;
00206     }
00207 
00208     return NS_OK;
00209 }
00210 
00211 
00212 NS_METHOD
00213 nsLoadGroup::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00214 {
00215     NS_ENSURE_ARG_POINTER(aResult);
00216     NS_ENSURE_PROPER_AGGREGATION(aOuter, aIID);
00217 
00218     nsresult rv;
00219     nsLoadGroup* group = new nsLoadGroup(aOuter);
00220     if (group == nsnull) {
00221         *aResult = nsnull;
00222         return NS_ERROR_OUT_OF_MEMORY;
00223     }
00224 
00225     rv = group->Init();
00226     if (NS_SUCCEEDED(rv)) {
00227         rv = group->AggregatedQueryInterface(aIID, aResult);
00228     }
00229 
00230     if (NS_FAILED(rv))
00231         delete group;
00232 
00233     return rv;
00234 }
00235 
00237 // nsISupports methods:
00238 
00239 NS_IMPL_AGGREGATED(nsLoadGroup)
00240 
00241 NS_IMETHODIMP
00242 nsLoadGroup::AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr)
00243 {
00244     NS_ENSURE_ARG_POINTER(aInstancePtr);
00245 
00246     if (aIID.Equals(NS_GET_IID(nsISupports)))
00247         *aInstancePtr = GetInner();
00248     else if (aIID.Equals(NS_GET_IID(nsILoadGroup)) ||
00249         aIID.Equals(NS_GET_IID(nsIRequest)) ||
00250         aIID.Equals(NS_GET_IID(nsISupports))) {
00251         *aInstancePtr = NS_STATIC_CAST(nsILoadGroup*, this);
00252     }
00253     else if (aIID.Equals(NS_GET_IID(nsISupportsPriority))) {
00254         *aInstancePtr = NS_STATIC_CAST(nsISupportsPriority*,this);
00255     }
00256     else if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) {
00257         *aInstancePtr = NS_STATIC_CAST(nsISupportsWeakReference*,this);
00258     }
00259     else {
00260         *aInstancePtr = nsnull;
00261         return NS_NOINTERFACE;
00262     }
00263 
00264     NS_ADDREF((nsISupports*)*aInstancePtr);
00265     return NS_OK;
00266 }
00267 
00269 // nsIRequest methods:
00270 
00271 NS_IMETHODIMP
00272 nsLoadGroup::GetName(nsACString &result)
00273 {
00274     // XXX is this the right "name" for a load group?
00275 
00276     if (!mDefaultLoadRequest) {
00277         result.Truncate();
00278         return NS_OK;
00279     }
00280     
00281     return mDefaultLoadRequest->GetName(result);
00282 }
00283 
00284 NS_IMETHODIMP
00285 nsLoadGroup::IsPending(PRBool *aResult)
00286 {
00287     *aResult = (mForegroundCount > 0) ? PR_TRUE : PR_FALSE;
00288     return NS_OK;
00289 }
00290 
00291 NS_IMETHODIMP
00292 nsLoadGroup::GetStatus(nsresult *status)
00293 {
00294     if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest)
00295         return mDefaultLoadRequest->GetStatus(status);
00296     
00297     *status = mStatus;
00298     return NS_OK; 
00299 }
00300 
00301 // PLDHashTable enumeration callback that appends strong references to
00302 // all nsIRequest to an nsVoidArray.
00303 PR_STATIC_CALLBACK(PLDHashOperator)
00304 AppendRequestsToVoidArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
00305                           PRUint32 number, void *arg)
00306 {
00307     RequestMapEntry *e = NS_STATIC_CAST(RequestMapEntry *, hdr);
00308     nsVoidArray *array = NS_STATIC_CAST(nsVoidArray *, arg);
00309 
00310     nsIRequest *request = e->mKey;
00311     NS_ASSERTION(request, "What? Null key in pldhash entry?");
00312 
00313     PRBool ok = array->AppendElement(request);
00314 
00315     if (!ok) {
00316         return PL_DHASH_STOP;
00317     }
00318 
00319     NS_ADDREF(request);
00320 
00321     return PL_DHASH_NEXT;
00322 }
00323 
00324 // nsVoidArray enumeration callback that releases all items in the
00325 // nsVoidArray
00326 PR_STATIC_CALLBACK(PRBool)
00327 ReleaseVoidArrayItems(void *aElement, void *aData)
00328 {
00329     nsISupports *s = NS_STATIC_CAST(nsISupports *, aElement);
00330 
00331     NS_RELEASE(s);
00332 
00333     return PR_TRUE;
00334 }
00335 
00336 NS_IMETHODIMP
00337 nsLoadGroup::Cancel(nsresult status)
00338 {
00339     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
00340     nsresult rv;
00341     PRUint32 count = mRequests.entryCount;
00342 
00343     nsAutoVoidArray requests;
00344 
00345     PL_DHashTableEnumerate(&mRequests, AppendRequestsToVoidArray,
00346                            NS_STATIC_CAST(nsVoidArray *, &requests));
00347 
00348     if (requests.Count() != (PRInt32)count) {
00349         requests.EnumerateForwards(ReleaseVoidArrayItems, nsnull);
00350 
00351         return NS_ERROR_OUT_OF_MEMORY;
00352     }
00353 
00354     // set the load group status to our cancel status while we cancel 
00355     // all our requests...once the cancel is done, we'll reset it...
00356     //
00357     mStatus = status;
00358 
00359     // Set the flag indicating that the loadgroup is being canceled...  This
00360     // prevents any new channels from being added during the operation.
00361     //
00362     mIsCanceling = PR_TRUE;
00363 
00364     nsresult firstError = NS_OK;
00365 
00366     while (count > 0) {
00367         nsIRequest* request = NS_STATIC_CAST(nsIRequest*, requests.ElementAt(--count));
00368 
00369         NS_ASSERTION(request, "NULL request found in list.");
00370 
00371         RequestMapEntry *entry =
00372             NS_STATIC_CAST(RequestMapEntry *,
00373                            PL_DHashTableOperate(&mRequests, request,
00374                                                 PL_DHASH_LOOKUP));
00375 
00376         if (PL_DHASH_ENTRY_IS_FREE(entry)) {
00377             // |request| was removed already
00378 
00379             NS_RELEASE(request);
00380 
00381             continue;
00382         }
00383 
00384 #if defined(PR_LOGGING)
00385         nsCAutoString nameStr;
00386         request->GetName(nameStr);
00387         LOG(("LOADGROUP [%x]: Canceling request %x %s.\n",
00388              this, request, nameStr.get()));
00389 #endif
00390 
00391         //
00392         // Remove the request from the load group...  This may cause
00393         // the OnStopRequest notification to fire...
00394         //
00395         // XXX: What should the context be?
00396         //
00397         (void)RemoveRequest(request, nsnull, status);
00398 
00399         // Cancel the request...
00400         rv = request->Cancel(status);
00401 
00402         // Remember the first failure and return it...
00403         if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
00404             firstError = rv;
00405 
00406         NS_RELEASE(request);
00407     }
00408 
00409 #if defined(DEBUG)
00410     NS_ASSERTION(mRequests.entryCount == 0, "Request list is not empty.");
00411     NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");
00412 #endif
00413 
00414     mStatus = NS_OK;
00415     mIsCanceling = PR_FALSE;
00416 
00417     return firstError;
00418 }
00419 
00420 
00421 NS_IMETHODIMP
00422 nsLoadGroup::Suspend()
00423 {
00424     nsresult rv, firstError;
00425     PRUint32 count = mRequests.entryCount;
00426 
00427     nsAutoVoidArray requests;
00428 
00429     PL_DHashTableEnumerate(&mRequests, AppendRequestsToVoidArray,
00430                            NS_STATIC_CAST(nsVoidArray *, &requests));
00431 
00432     if (requests.Count() != (PRInt32)count) {
00433         requests.EnumerateForwards(ReleaseVoidArrayItems, nsnull);
00434 
00435         return NS_ERROR_OUT_OF_MEMORY;
00436     }
00437 
00438     firstError = NS_OK;
00439     //
00440     // Operate the elements from back to front so that if items get
00441     // get removed from the list it won't affect our iteration
00442     //
00443     while (count > 0) {
00444         nsIRequest* request =
00445             NS_STATIC_CAST(nsIRequest*, requests.ElementAt(--count));
00446 
00447         NS_ASSERTION(request, "NULL request found in list.");
00448         if (!request)
00449             continue;
00450 
00451 #if defined(PR_LOGGING)
00452         nsCAutoString nameStr;
00453         request->GetName(nameStr);
00454         LOG(("LOADGROUP [%x]: Suspending request %x %s.\n",
00455             this, request, nameStr.get()));
00456 #endif
00457 
00458         // Suspend the request...
00459         rv = request->Suspend();
00460 
00461         // Remember the first failure and return it...
00462         if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
00463             firstError = rv;
00464 
00465         NS_RELEASE(request);
00466     }
00467 
00468     return firstError;
00469 }
00470 
00471 
00472 NS_IMETHODIMP
00473 nsLoadGroup::Resume()
00474 {
00475     nsresult rv, firstError;
00476     PRUint32 count = mRequests.entryCount;
00477 
00478     nsAutoVoidArray requests;
00479 
00480     PL_DHashTableEnumerate(&mRequests, AppendRequestsToVoidArray,
00481                            NS_STATIC_CAST(nsVoidArray *, &requests));
00482 
00483     if (requests.Count() != (PRInt32)count) {
00484         requests.EnumerateForwards(ReleaseVoidArrayItems, nsnull);
00485 
00486         return NS_ERROR_OUT_OF_MEMORY;
00487     }
00488 
00489     firstError = NS_OK;
00490     //
00491     // Operate the elements from back to front so that if items get
00492     // get removed from the list it won't affect our iteration
00493     //
00494     while (count > 0) {
00495         nsIRequest* request =
00496             NS_STATIC_CAST(nsIRequest*, requests.ElementAt(--count));
00497 
00498         NS_ASSERTION(request, "NULL request found in list.");
00499         if (!request)
00500             continue;
00501 
00502 #if defined(PR_LOGGING)
00503         nsCAutoString nameStr;
00504         request->GetName(nameStr);
00505         LOG(("LOADGROUP [%x]: Resuming request %x %s.\n",
00506             this, request, nameStr.get()));
00507 #endif
00508 
00509         // Resume the request...
00510         rv = request->Resume();
00511 
00512         // Remember the first failure and return it...
00513         if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
00514             firstError = rv;
00515 
00516         NS_RELEASE(request);
00517     }
00518 
00519     return firstError;
00520 }
00521 
00522 NS_IMETHODIMP
00523 nsLoadGroup::GetLoadFlags(PRUint32 *aLoadFlags)
00524 {
00525     *aLoadFlags = mLoadFlags;
00526     return NS_OK;
00527 }
00528 
00529 NS_IMETHODIMP
00530 nsLoadGroup::SetLoadFlags(PRUint32 aLoadFlags)
00531 {
00532     mLoadFlags = aLoadFlags;
00533     return NS_OK;
00534 }
00535 
00536 NS_IMETHODIMP
00537 nsLoadGroup::GetLoadGroup(nsILoadGroup **loadGroup)
00538 {
00539     *loadGroup = mLoadGroup;
00540     NS_IF_ADDREF(*loadGroup);
00541     return NS_OK;
00542 }
00543 
00544 NS_IMETHODIMP
00545 nsLoadGroup::SetLoadGroup(nsILoadGroup *loadGroup)
00546 {
00547     mLoadGroup = loadGroup;
00548     return NS_OK;
00549 }
00550 
00552 // nsILoadGroup methods:
00553 
00554 NS_IMETHODIMP
00555 nsLoadGroup::GetDefaultLoadRequest(nsIRequest * *aRequest)
00556 {
00557     *aRequest = mDefaultLoadRequest;
00558     NS_IF_ADDREF(*aRequest);
00559     return NS_OK;
00560 }
00561 
00562 NS_IMETHODIMP
00563 nsLoadGroup::SetDefaultLoadRequest(nsIRequest *aRequest)
00564 {
00565     mDefaultLoadRequest = aRequest;
00566     // Inherit the group load flags from the default load request
00567     if (mDefaultLoadRequest) {
00568         mDefaultLoadRequest->GetLoadFlags(&mLoadFlags);
00569         //
00570         // Mask off any bits that are not part of the nsIRequest flags.
00571         // in particular, nsIChannel::LOAD_DOCUMENT_URI...
00572         //
00573         mLoadFlags &= 0xFFFF;
00574     }
00575     // Else, do not change the group's load flags (see bug 95981)
00576     return NS_OK;
00577 }
00578 
00579 NS_IMETHODIMP
00580 nsLoadGroup::AddRequest(nsIRequest *request, nsISupports* ctxt)
00581 {
00582     nsresult rv;
00583 
00584 #if defined(PR_LOGGING)
00585     {
00586         nsCAutoString nameStr;
00587         request->GetName(nameStr);
00588         LOG(("LOADGROUP [%x]: Adding request %x %s (count=%d).\n",
00589              this, request, nameStr.get(), mRequests.entryCount));
00590     }
00591 #endif /* PR_LOGGING */
00592 
00593     //
00594     // Do not add the channel, if the loadgroup is being canceled...
00595     //
00596     if (mIsCanceling) {
00597 
00598 #if defined(PR_LOGGING)
00599         LOG(("LOADGROUP [%x]: AddChannel() ABORTED because LoadGroup is"
00600              " being canceled!!\n", this));
00601 #endif /* PR_LOGGING */
00602 
00603         return NS_BINDING_ABORTED;
00604     }
00605 
00606     nsLoadFlags flags;
00607     // if the request is the default load request or if the default
00608     // load request is null, then the load group should inherit its
00609     // load flags from the request.
00610     if (mDefaultLoadRequest == request || !mDefaultLoadRequest)
00611         rv = request->GetLoadFlags(&flags);
00612     else
00613         rv = MergeLoadFlags(request, flags);
00614     if (NS_FAILED(rv)) return rv;
00615     
00616     //
00617     // Add the request to the list of active requests...
00618     //
00619 
00620     RequestMapEntry *entry =
00621         NS_STATIC_CAST(RequestMapEntry *,
00622                        PL_DHashTableOperate(&mRequests, request,
00623                                         PL_DHASH_ADD));
00624 
00625     if (!entry) {
00626         return NS_ERROR_OUT_OF_MEMORY;
00627     }
00628 
00629     if (mPriority != 0)
00630         RescheduleRequest(request, mPriority);
00631 
00632     if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
00633         // Update the count of foreground URIs..
00634         mForegroundCount += 1;
00635 
00636         //
00637         // Fire the OnStartRequest notification out to the observer...
00638         //
00639         // If the notification fails then DO NOT add the request to
00640         // the load group.
00641         //
00642         nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
00643         if (observer) {
00644             LOG(("LOADGROUP [%x]: Firing OnStartRequest for request %x."
00645                  "(foreground count=%d).\n", this, request, mForegroundCount));
00646 
00647             rv = observer->OnStartRequest(request, ctxt);
00648             if (NS_FAILED(rv)) {
00649                 LOG(("LOADGROUP [%x]: OnStartRequest for request %x FAILED.\n",
00650                     this, request));
00651                 //
00652                 // The URI load has been canceled by the observer.  Clean up
00653                 // the damage...
00654                 //
00655 
00656                 PL_DHashTableOperate(&mRequests, request, PL_DHASH_REMOVE);
00657 
00658                 rv = NS_OK;
00659 
00660                 mForegroundCount -= 1;
00661             }
00662         }
00663 
00664         // Ensure that we're part of our loadgroup while pending
00665         if (mForegroundCount == 1 && mLoadGroup) {
00666             mLoadGroup->AddRequest(this, nsnull);
00667         }
00668 
00669     }
00670 
00671     return rv;
00672 }
00673 
00674 NS_IMETHODIMP
00675 nsLoadGroup::RemoveRequest(nsIRequest *request, nsISupports* ctxt,
00676                            nsresult aStatus)
00677 {
00678     NS_ENSURE_ARG_POINTER(request);
00679     nsresult rv;
00680 
00681 #if defined(PR_LOGGING)
00682     {
00683         nsCAutoString nameStr;
00684         request->GetName(nameStr);
00685         LOG(("LOADGROUP [%x]: Removing request %x %s status %x (count=%d).\n",
00686             this, request, nameStr.get(), aStatus, mRequests.entryCount-1));
00687     }
00688 #endif
00689 
00690     // Make sure we have a owning reference to the request we're about
00691     // to remove.
00692 
00693     nsCOMPtr<nsIRequest> kungFuDeathGrip(request);
00694 
00695     //
00696     // Remove the request from the group.  If this fails, it means that
00697     // the request was *not* in the group so do not update the foreground
00698     // count or it will get messed up...
00699     //
00700     RequestMapEntry *entry =
00701         NS_STATIC_CAST(RequestMapEntry *,
00702                        PL_DHashTableOperate(&mRequests, request,
00703                                         PL_DHASH_LOOKUP));
00704 
00705     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
00706         LOG(("LOADGROUP [%x]: Unable to remove request %x. Not in group!\n",
00707             this, request));
00708 
00709         return NS_ERROR_FAILURE;
00710     }
00711 
00712     PL_DHashTableRawRemove(&mRequests, entry);
00713 
00714     // Undo any group priority delta...
00715     if (mPriority != 0)
00716         RescheduleRequest(request, -mPriority);
00717 
00718     nsLoadFlags flags;
00719     rv = request->GetLoadFlags(&flags);
00720     if (NS_FAILED(rv)) return rv;
00721 
00722     if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
00723         NS_ASSERTION(mForegroundCount > 0, "ForegroundCount messed up");
00724         mForegroundCount -= 1;
00725 
00726         // Fire the OnStopRequest out to the observer...
00727         nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
00728         if (observer) {
00729             LOG(("LOADGROUP [%x]: Firing OnStopRequest for request %x."
00730                  "(foreground count=%d).\n", this, request, mForegroundCount));
00731 
00732             rv = observer->OnStopRequest(request, ctxt, aStatus);
00733 
00734 #if defined(PR_LOGGING)
00735             if (NS_FAILED(rv)) {
00736                 LOG(("LOADGROUP [%x]: OnStopRequest for request %x FAILED.\n",
00737                     this, request));
00738             }
00739 #endif
00740         }
00741 
00742         // If that was the last request -> remove ourselves from loadgroup
00743         if (mForegroundCount == 0 && mLoadGroup) {
00744             mLoadGroup->RemoveRequest(this, nsnull, aStatus);
00745         }
00746     }
00747 
00748     return rv;
00749 }
00750 
00751 // PLDHashTable enumeration callback that appends all items in the
00752 // hash to an nsISupportsArray.
00753 PR_STATIC_CALLBACK(PLDHashOperator)
00754 AppendRequestsToISupportsArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
00755                                PRUint32 number, void *arg)
00756 {
00757     RequestMapEntry *e = NS_STATIC_CAST(RequestMapEntry *, hdr);
00758     nsISupportsArray *array = NS_STATIC_CAST(nsISupportsArray *, arg);
00759 
00760     PRBool ok = array->AppendElement(e->mKey);
00761 
00762     if (!ok) {
00763         return PL_DHASH_STOP;
00764     }
00765 
00766     return PL_DHASH_NEXT;
00767 }
00768 
00769 NS_IMETHODIMP
00770 nsLoadGroup::GetRequests(nsISimpleEnumerator * *aRequests)
00771 {
00772     nsCOMPtr<nsISupportsArray> array;
00773     nsresult rv = NS_NewISupportsArray(getter_AddRefs(array));
00774     NS_ENSURE_SUCCESS(rv, rv);
00775 
00776     PL_DHashTableEnumerate(&mRequests, AppendRequestsToISupportsArray,
00777                            array.get());
00778 
00779     PRUint32 count;
00780     array->Count(&count);
00781 
00782     if (count != mRequests.entryCount) {
00783         return NS_ERROR_OUT_OF_MEMORY;
00784     }
00785 
00786     return NS_NewArrayEnumerator(aRequests, array);
00787 }
00788 
00789 NS_IMETHODIMP
00790 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver)
00791 {
00792     mObserver = do_GetWeakReference(aObserver);
00793     return NS_OK;
00794 }
00795 
00796 NS_IMETHODIMP
00797 nsLoadGroup::GetGroupObserver(nsIRequestObserver* *aResult)
00798 {
00799     nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
00800     *aResult = observer;
00801     NS_IF_ADDREF(*aResult);
00802     return NS_OK;
00803 }
00804 
00805 NS_IMETHODIMP
00806 nsLoadGroup::GetActiveCount(PRUint32* aResult)
00807 {
00808     *aResult = mForegroundCount;
00809     return NS_OK;
00810 }
00811 
00812 NS_IMETHODIMP
00813 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
00814 {
00815     NS_ENSURE_ARG_POINTER(aCallbacks);
00816     *aCallbacks = mCallbacks;
00817     NS_IF_ADDREF(*aCallbacks);
00818     return NS_OK;
00819 }
00820 
00821 NS_IMETHODIMP
00822 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
00823 {
00824     mCallbacks = aCallbacks;
00825     return NS_OK;
00826 }
00827 
00829 // nsISupportsPriority methods:
00830 
00831 NS_IMETHODIMP
00832 nsLoadGroup::GetPriority(PRInt32 *aValue)
00833 {
00834     *aValue = mPriority;
00835     return NS_OK;
00836 }
00837 
00838 NS_IMETHODIMP
00839 nsLoadGroup::SetPriority(PRInt32 aValue)
00840 {
00841     return AdjustPriority(aValue - mPriority);
00842 }
00843 
00844 NS_IMETHODIMP
00845 nsLoadGroup::AdjustPriority(PRInt32 aDelta)
00846 {
00847     // Update the priority for each request that supports nsISupportsPriority
00848     if (aDelta != 0) {
00849         mPriority += aDelta;
00850         PL_DHashTableEnumerate(&mRequests, RescheduleRequests, &aDelta);
00851     }
00852     return NS_OK;
00853 }
00854 
00856 
00857 nsresult nsLoadGroup::MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& outFlags)
00858 {
00859     nsresult rv;
00860     nsLoadFlags flags, oldFlags;
00861 
00862     rv = aRequest->GetLoadFlags(&flags);
00863     if (NS_FAILED(rv)) 
00864         return rv;
00865 
00866     oldFlags = flags;
00867 
00868     // Inherit the following bits...
00869     flags |= (mLoadFlags & (LOAD_BACKGROUND |
00870                             LOAD_BYPASS_CACHE |
00871                             LOAD_FROM_CACHE |
00872                             VALIDATE_ALWAYS |
00873                             VALIDATE_ONCE_PER_SESSION |
00874                             VALIDATE_NEVER));
00875 
00876     if (flags != oldFlags)
00877         rv = aRequest->SetLoadFlags(flags);
00878 
00879     outFlags = flags;
00880     return rv;
00881 }