1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set sw=4 ts=4 sts=4 et cin: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Darin Fisher <darin@netscape.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsLoadGroup.h"
41 #include "nsISupportsArray.h"
42 #include "nsEnumeratorUtils.h"
43 #include "nsIServiceManager.h"
49 #include "nsXPIDLString.h"
50 #include "nsReadableUtils.h"
52 #include "nsVoidArray.h"
54 #if defined(PR_LOGGING)
56 // Log module for nsILoadGroup logging...
58 // To enable logging (see prlog.h for full details):
60 // set NSPR_LOG_MODULES=LoadGroup:5
61 // set NSPR_LOG_FILE=nspr.log
63 // this enables PR_LOG_DEBUG level information and places all output in
66 static PRLogModuleInfo
* gLoadGroupLog
= nsnull
;
69 #define LOG(args) PR_LOG(gLoadGroupLog, PR_LOG_DEBUG, args)
71 ////////////////////////////////////////////////////////////////////////////////
73 class RequestMapEntry
: public PLDHashEntryHdr
76 RequestMapEntry(nsIRequest
*aRequest
) :
81 nsCOMPtr
<nsIRequest
> mKey
;
85 RequestHashMatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*entry
,
88 const RequestMapEntry
*e
=
89 static_cast<const RequestMapEntry
*>(entry
);
90 const nsIRequest
*request
= static_cast<const nsIRequest
*>(key
);
92 return e
->mKey
== request
;
96 RequestHashClearEntry(PLDHashTable
*table
, PLDHashEntryHdr
*entry
)
98 RequestMapEntry
*e
= static_cast<RequestMapEntry
*>(entry
);
100 // An entry is being cleared, let the entry do its own cleanup.
101 e
->~RequestMapEntry();
105 RequestHashInitEntry(PLDHashTable
*table
, PLDHashEntryHdr
*entry
,
108 const nsIRequest
*const_request
= static_cast<const nsIRequest
*>(key
);
109 nsIRequest
*request
= const_cast<nsIRequest
*>(const_request
);
111 // Initialize the entry with placement new
112 new (entry
) RequestMapEntry(request
);
118 RescheduleRequest(nsIRequest
*aRequest
, PRInt32 delta
)
120 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(aRequest
);
122 p
->AdjustPriority(delta
);
125 static PLDHashOperator
126 RescheduleRequests(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
127 PRUint32 number
, void *arg
)
129 RequestMapEntry
*e
= static_cast<RequestMapEntry
*>(hdr
);
130 PRInt32
*delta
= static_cast<PRInt32
*>(arg
);
132 RescheduleRequest(e
->mKey
, *delta
);
133 return PL_DHASH_NEXT
;
137 nsLoadGroup::nsLoadGroup(nsISupports
* outer
)
138 : mForegroundCount(0)
139 , mLoadFlags(LOAD_NORMAL
)
141 , mPriority(PRIORITY_NORMAL
)
142 , mIsCanceling(PR_FALSE
)
144 NS_INIT_AGGREGATED(outer
);
146 #if defined(PR_LOGGING)
147 // Initialize the global PRLogModule for nsILoadGroup logging
148 if (nsnull
== gLoadGroupLog
)
149 gLoadGroupLog
= PR_NewLogModule("LoadGroup");
152 LOG(("LOADGROUP [%x]: Created.\n", this));
154 // Initialize the ops in the hash to null to make sure we get
155 // consistent errors if someone fails to call ::Init() on an
157 mRequests
.ops
= nsnull
;
160 nsLoadGroup::~nsLoadGroup()
164 rv
= Cancel(NS_BINDING_ABORTED
);
165 NS_ASSERTION(NS_SUCCEEDED(rv
), "Cancel failed");
168 PL_DHashTableFinish(&mRequests
);
171 mDefaultLoadRequest
= 0;
173 LOG(("LOADGROUP [%x]: Destroyed.\n", this));
177 nsresult
nsLoadGroup::Init()
179 static PLDHashTableOps hash_table_ops
=
183 PL_DHashVoidPtrKeyStub
,
184 RequestHashMatchEntry
,
185 PL_DHashMoveEntryStub
,
186 RequestHashClearEntry
,
187 PL_DHashFinalizeStub
,
191 if (!PL_DHashTableInit(&mRequests
, &hash_table_ops
, nsnull
,
192 sizeof(RequestMapEntry
), 16)) {
193 mRequests
.ops
= nsnull
;
195 return NS_ERROR_OUT_OF_MEMORY
;
201 ////////////////////////////////////////////////////////////////////////////////
202 // nsISupports methods:
204 NS_IMPL_AGGREGATED(nsLoadGroup
)
205 NS_INTERFACE_MAP_BEGIN_AGGREGATED(nsLoadGroup
)
206 NS_INTERFACE_MAP_ENTRY(nsILoadGroup
)
207 NS_INTERFACE_MAP_ENTRY(nsIRequest
)
208 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority
)
209 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
212 ////////////////////////////////////////////////////////////////////////////////
213 // nsIRequest methods:
216 nsLoadGroup::GetName(nsACString
&result
)
218 // XXX is this the right "name" for a load group?
220 if (!mDefaultLoadRequest
) {
225 return mDefaultLoadRequest
->GetName(result
);
229 nsLoadGroup::IsPending(PRBool
*aResult
)
231 *aResult
= (mForegroundCount
> 0) ? PR_TRUE
: PR_FALSE
;
236 nsLoadGroup::GetStatus(nsresult
*status
)
238 if (NS_SUCCEEDED(mStatus
) && mDefaultLoadRequest
)
239 return mDefaultLoadRequest
->GetStatus(status
);
245 // PLDHashTable enumeration callback that appends strong references to
246 // all nsIRequest to an nsVoidArray.
247 static PLDHashOperator
248 AppendRequestsToVoidArray(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
249 PRUint32 number
, void *arg
)
251 RequestMapEntry
*e
= static_cast<RequestMapEntry
*>(hdr
);
252 nsVoidArray
*array
= static_cast<nsVoidArray
*>(arg
);
254 nsIRequest
*request
= e
->mKey
;
255 NS_ASSERTION(request
, "What? Null key in pldhash entry?");
257 PRBool ok
= array
->AppendElement(request
);
260 return PL_DHASH_STOP
;
265 return PL_DHASH_NEXT
;
268 // nsVoidArray enumeration callback that releases all items in the
271 ReleaseVoidArrayItems(void *aElement
, void *aData
)
273 nsISupports
*s
= static_cast<nsISupports
*>(aElement
);
281 nsLoadGroup::Cancel(nsresult status
)
283 NS_ASSERTION(NS_FAILED(status
), "shouldn't cancel with a success code");
285 PRUint32 count
= mRequests
.entryCount
;
287 nsAutoVoidArray requests
;
289 PL_DHashTableEnumerate(&mRequests
, AppendRequestsToVoidArray
,
290 static_cast<nsVoidArray
*>(&requests
));
292 if (requests
.Count() != (PRInt32
)count
) {
293 requests
.EnumerateForwards(ReleaseVoidArrayItems
, nsnull
);
295 return NS_ERROR_OUT_OF_MEMORY
;
298 // set the load group status to our cancel status while we cancel
299 // all our requests...once the cancel is done, we'll reset it...
303 // Set the flag indicating that the loadgroup is being canceled... This
304 // prevents any new channels from being added during the operation.
306 mIsCanceling
= PR_TRUE
;
308 nsresult firstError
= NS_OK
;
311 nsIRequest
* request
= static_cast<nsIRequest
*>(requests
.ElementAt(--count
));
313 NS_ASSERTION(request
, "NULL request found in list.");
315 RequestMapEntry
*entry
=
316 static_cast<RequestMapEntry
*>
317 (PL_DHashTableOperate(&mRequests
, request
,
320 if (PL_DHASH_ENTRY_IS_FREE(entry
)) {
321 // |request| was removed already
328 #if defined(PR_LOGGING)
329 nsCAutoString nameStr
;
330 request
->GetName(nameStr
);
331 LOG(("LOADGROUP [%x]: Canceling request %x %s.\n",
332 this, request
, nameStr
.get()));
336 // Remove the request from the load group... This may cause
337 // the OnStopRequest notification to fire...
339 // XXX: What should the context be?
341 (void)RemoveRequest(request
, nsnull
, status
);
343 // Cancel the request...
344 rv
= request
->Cancel(status
);
346 // Remember the first failure and return it...
347 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
))
354 NS_ASSERTION(mRequests
.entryCount
== 0, "Request list is not empty.");
355 NS_ASSERTION(mForegroundCount
== 0, "Foreground URLs are active.");
359 mIsCanceling
= PR_FALSE
;
366 nsLoadGroup::Suspend()
368 nsresult rv
, firstError
;
369 PRUint32 count
= mRequests
.entryCount
;
371 nsAutoVoidArray requests
;
373 PL_DHashTableEnumerate(&mRequests
, AppendRequestsToVoidArray
,
374 static_cast<nsVoidArray
*>(&requests
));
376 if (requests
.Count() != (PRInt32
)count
) {
377 requests
.EnumerateForwards(ReleaseVoidArrayItems
, nsnull
);
379 return NS_ERROR_OUT_OF_MEMORY
;
384 // Operate the elements from back to front so that if items get
385 // get removed from the list it won't affect our iteration
388 nsIRequest
* request
=
389 static_cast<nsIRequest
*>(requests
.ElementAt(--count
));
391 NS_ASSERTION(request
, "NULL request found in list.");
395 #if defined(PR_LOGGING)
396 nsCAutoString nameStr
;
397 request
->GetName(nameStr
);
398 LOG(("LOADGROUP [%x]: Suspending request %x %s.\n",
399 this, request
, nameStr
.get()));
402 // Suspend the request...
403 rv
= request
->Suspend();
405 // Remember the first failure and return it...
406 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
))
417 nsLoadGroup::Resume()
419 nsresult rv
, firstError
;
420 PRUint32 count
= mRequests
.entryCount
;
422 nsAutoVoidArray requests
;
424 PL_DHashTableEnumerate(&mRequests
, AppendRequestsToVoidArray
,
425 static_cast<nsVoidArray
*>(&requests
));
427 if (requests
.Count() != (PRInt32
)count
) {
428 requests
.EnumerateForwards(ReleaseVoidArrayItems
, nsnull
);
430 return NS_ERROR_OUT_OF_MEMORY
;
435 // Operate the elements from back to front so that if items get
436 // get removed from the list it won't affect our iteration
439 nsIRequest
* request
=
440 static_cast<nsIRequest
*>(requests
.ElementAt(--count
));
442 NS_ASSERTION(request
, "NULL request found in list.");
446 #if defined(PR_LOGGING)
447 nsCAutoString nameStr
;
448 request
->GetName(nameStr
);
449 LOG(("LOADGROUP [%x]: Resuming request %x %s.\n",
450 this, request
, nameStr
.get()));
453 // Resume the request...
454 rv
= request
->Resume();
456 // Remember the first failure and return it...
457 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
))
467 nsLoadGroup::GetLoadFlags(PRUint32
*aLoadFlags
)
469 *aLoadFlags
= mLoadFlags
;
474 nsLoadGroup::SetLoadFlags(PRUint32 aLoadFlags
)
476 mLoadFlags
= aLoadFlags
;
481 nsLoadGroup::GetLoadGroup(nsILoadGroup
**loadGroup
)
483 *loadGroup
= mLoadGroup
;
484 NS_IF_ADDREF(*loadGroup
);
489 nsLoadGroup::SetLoadGroup(nsILoadGroup
*loadGroup
)
491 mLoadGroup
= loadGroup
;
495 ////////////////////////////////////////////////////////////////////////////////
496 // nsILoadGroup methods:
499 nsLoadGroup::GetDefaultLoadRequest(nsIRequest
* *aRequest
)
501 *aRequest
= mDefaultLoadRequest
;
502 NS_IF_ADDREF(*aRequest
);
507 nsLoadGroup::SetDefaultLoadRequest(nsIRequest
*aRequest
)
509 mDefaultLoadRequest
= aRequest
;
510 // Inherit the group load flags from the default load request
511 if (mDefaultLoadRequest
) {
512 mDefaultLoadRequest
->GetLoadFlags(&mLoadFlags
);
514 // Mask off any bits that are not part of the nsIRequest flags.
515 // in particular, nsIChannel::LOAD_DOCUMENT_URI...
517 mLoadFlags
&= 0xFFFF;
519 // Else, do not change the group's load flags (see bug 95981)
524 nsLoadGroup::AddRequest(nsIRequest
*request
, nsISupports
* ctxt
)
528 #if defined(PR_LOGGING)
530 nsCAutoString nameStr
;
531 request
->GetName(nameStr
);
532 LOG(("LOADGROUP [%x]: Adding request %x %s (count=%d).\n",
533 this, request
, nameStr
.get(), mRequests
.entryCount
));
535 #endif /* PR_LOGGING */
539 RequestMapEntry
*entry
=
540 static_cast<RequestMapEntry
*>
541 (PL_DHashTableOperate(&mRequests
, request
,
544 NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(entry
),
545 "Entry added to loadgroup twice, don't do that");
550 // Do not add the channel, if the loadgroup is being canceled...
554 #if defined(PR_LOGGING)
555 LOG(("LOADGROUP [%x]: AddChannel() ABORTED because LoadGroup is"
556 " being canceled!!\n", this));
557 #endif /* PR_LOGGING */
559 return NS_BINDING_ABORTED
;
563 // if the request is the default load request or if the default
564 // load request is null, then the load group should inherit its
565 // load flags from the request.
566 if (mDefaultLoadRequest
== request
|| !mDefaultLoadRequest
)
567 rv
= request
->GetLoadFlags(&flags
);
569 rv
= MergeLoadFlags(request
, flags
);
570 if (NS_FAILED(rv
)) return rv
;
573 // Add the request to the list of active requests...
576 RequestMapEntry
*entry
=
577 static_cast<RequestMapEntry
*>
578 (PL_DHashTableOperate(&mRequests
, request
,
582 return NS_ERROR_OUT_OF_MEMORY
;
586 RescheduleRequest(request
, mPriority
);
588 if (!(flags
& nsIRequest::LOAD_BACKGROUND
)) {
589 // Update the count of foreground URIs..
590 mForegroundCount
+= 1;
593 // Fire the OnStartRequest notification out to the observer...
595 // If the notification fails then DO NOT add the request to
598 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
600 LOG(("LOADGROUP [%x]: Firing OnStartRequest for request %x."
601 "(foreground count=%d).\n", this, request
, mForegroundCount
));
603 rv
= observer
->OnStartRequest(request
, ctxt
);
605 LOG(("LOADGROUP [%x]: OnStartRequest for request %x FAILED.\n",
608 // The URI load has been canceled by the observer. Clean up
612 PL_DHashTableOperate(&mRequests
, request
, PL_DHASH_REMOVE
);
616 mForegroundCount
-= 1;
620 // Ensure that we're part of our loadgroup while pending
621 if (mForegroundCount
== 1 && mLoadGroup
) {
622 mLoadGroup
->AddRequest(this, nsnull
);
631 nsLoadGroup::RemoveRequest(nsIRequest
*request
, nsISupports
* ctxt
,
634 NS_ENSURE_ARG_POINTER(request
);
637 #if defined(PR_LOGGING)
639 nsCAutoString nameStr
;
640 request
->GetName(nameStr
);
641 LOG(("LOADGROUP [%x]: Removing request %x %s status %x (count=%d).\n",
642 this, request
, nameStr
.get(), aStatus
, mRequests
.entryCount
-1));
646 // Make sure we have a owning reference to the request we're about
649 nsCOMPtr
<nsIRequest
> kungFuDeathGrip(request
);
652 // Remove the request from the group. If this fails, it means that
653 // the request was *not* in the group so do not update the foreground
654 // count or it will get messed up...
656 RequestMapEntry
*entry
=
657 static_cast<RequestMapEntry
*>
658 (PL_DHashTableOperate(&mRequests
, request
,
661 if (PL_DHASH_ENTRY_IS_FREE(entry
)) {
662 LOG(("LOADGROUP [%x]: Unable to remove request %x. Not in group!\n",
665 return NS_ERROR_FAILURE
;
668 PL_DHashTableRawRemove(&mRequests
, entry
);
670 // Undo any group priority delta...
672 RescheduleRequest(request
, -mPriority
);
675 rv
= request
->GetLoadFlags(&flags
);
676 if (NS_FAILED(rv
)) return rv
;
678 if (!(flags
& nsIRequest::LOAD_BACKGROUND
)) {
679 NS_ASSERTION(mForegroundCount
> 0, "ForegroundCount messed up");
680 mForegroundCount
-= 1;
682 // Fire the OnStopRequest out to the observer...
683 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
685 LOG(("LOADGROUP [%x]: Firing OnStopRequest for request %x."
686 "(foreground count=%d).\n", this, request
, mForegroundCount
));
688 rv
= observer
->OnStopRequest(request
, ctxt
, aStatus
);
690 #if defined(PR_LOGGING)
692 LOG(("LOADGROUP [%x]: OnStopRequest for request %x FAILED.\n",
698 // If that was the last request -> remove ourselves from loadgroup
699 if (mForegroundCount
== 0 && mLoadGroup
) {
700 mLoadGroup
->RemoveRequest(this, nsnull
, aStatus
);
707 // PLDHashTable enumeration callback that appends all items in the
708 // hash to an nsISupportsArray.
709 static PLDHashOperator
710 AppendRequestsToISupportsArray(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
711 PRUint32 number
, void *arg
)
713 RequestMapEntry
*e
= static_cast<RequestMapEntry
*>(hdr
);
714 nsISupportsArray
*array
= static_cast<nsISupportsArray
*>(arg
);
716 PRBool ok
= array
->AppendElement(e
->mKey
);
719 return PL_DHASH_STOP
;
722 return PL_DHASH_NEXT
;
726 nsLoadGroup::GetRequests(nsISimpleEnumerator
* *aRequests
)
728 nsCOMPtr
<nsISupportsArray
> array
;
729 nsresult rv
= NS_NewISupportsArray(getter_AddRefs(array
));
730 NS_ENSURE_SUCCESS(rv
, rv
);
732 PL_DHashTableEnumerate(&mRequests
, AppendRequestsToISupportsArray
,
736 array
->Count(&count
);
738 if (count
!= mRequests
.entryCount
) {
739 return NS_ERROR_OUT_OF_MEMORY
;
742 return NS_NewArrayEnumerator(aRequests
, array
);
746 nsLoadGroup::SetGroupObserver(nsIRequestObserver
* aObserver
)
748 mObserver
= do_GetWeakReference(aObserver
);
753 nsLoadGroup::GetGroupObserver(nsIRequestObserver
* *aResult
)
755 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
757 NS_IF_ADDREF(*aResult
);
762 nsLoadGroup::GetActiveCount(PRUint32
* aResult
)
764 *aResult
= mForegroundCount
;
769 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor
**aCallbacks
)
771 NS_ENSURE_ARG_POINTER(aCallbacks
);
772 *aCallbacks
= mCallbacks
;
773 NS_IF_ADDREF(*aCallbacks
);
778 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor
*aCallbacks
)
780 mCallbacks
= aCallbacks
;
784 ////////////////////////////////////////////////////////////////////////////////
785 // nsISupportsPriority methods:
788 nsLoadGroup::GetPriority(PRInt32
*aValue
)
795 nsLoadGroup::SetPriority(PRInt32 aValue
)
797 return AdjustPriority(aValue
- mPriority
);
801 nsLoadGroup::AdjustPriority(PRInt32 aDelta
)
803 // Update the priority for each request that supports nsISupportsPriority
806 PL_DHashTableEnumerate(&mRequests
, RescheduleRequests
, &aDelta
);
811 ////////////////////////////////////////////////////////////////////////////////
813 nsresult
nsLoadGroup::MergeLoadFlags(nsIRequest
*aRequest
, nsLoadFlags
& outFlags
)
816 nsLoadFlags flags
, oldFlags
;
818 rv
= aRequest
->GetLoadFlags(&flags
);
824 // Inherit the following bits...
825 flags
|= (mLoadFlags
& (LOAD_BACKGROUND
|
829 VALIDATE_ONCE_PER_SESSION
|
832 if (flags
!= oldFlags
)
833 rv
= aRequest
->SetLoadFlags(flags
);