Bug 627938: Fix nsGlobalChromeWindow cleanup. (r=smaug, a=jst)
[mozilla-central.git] / uriloader / base / nsDocLoader.cpp
blob78e4d9b1c76ba416148498348dc3a1d7b6d11d87
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nspr.h"
39 #include "prlog.h"
41 #include "nsDocLoader.h"
42 #include "nsCURILoader.h"
43 #include "nsNetUtil.h"
44 #include "nsIHttpChannel.h"
45 #include "nsIWebProgressListener2.h"
47 #include "nsIServiceManager.h"
48 #include "nsXPIDLString.h"
50 #include "nsIURL.h"
51 #include "nsCOMPtr.h"
52 #include "nscore.h"
53 #include "nsWeakPtr.h"
54 #include "nsAutoPtr.h"
56 #include "nsIDOMWindow.h"
58 #include "nsIStringBundle.h"
59 #include "nsIScriptSecurityManager.h"
61 #include "nsITransport.h"
62 #include "nsISocketTransport.h"
64 #include "nsIDOMDocument.h"
65 #include "nsIDocument.h"
66 #include "nsPresContext.h"
67 #include "nsIAsyncVerifyRedirectCallback.h"
69 static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
71 #if defined(PR_LOGGING)
73 // Log module for nsIDocumentLoader logging...
75 // To enable logging (see prlog.h for full details):
77 // set NSPR_LOG_MODULES=DocLoader:5
78 // set NSPR_LOG_FILE=nspr.log
80 // this enables PR_LOG_DEBUG level information and places all output in
81 // the file nspr.log
83 PRLogModuleInfo* gDocLoaderLog = nsnull;
84 #endif /* PR_LOGGING */
87 #if defined(DEBUG)
88 void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
90 if (request)
91 request->GetName(name);
92 else
93 name.AssignLiteral("???");
95 #endif /* DEBUG */
97 struct nsRequestInfo : public PLDHashEntryHdr
99 nsRequestInfo(const void *key)
100 : mKey(key), mCurrentProgress(0), mMaxProgress(0), mUploading(PR_FALSE)
101 , mIsDone(PR_FALSE)
105 nsIRequest* Request() {
106 return static_cast<nsIRequest*>(const_cast<void*>(mKey));
109 const void* mKey; // Must be first for the pldhash stubs to work
110 nsInt64 mCurrentProgress;
111 nsInt64 mMaxProgress;
112 PRBool mUploading;
114 PRBool mIsDone;
115 nsString mLastStatus;
116 nsresult mLastStatusCode;
120 static PRBool
121 RequestInfoHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
122 const void *key)
124 // Initialize the entry with placement new
125 new (entry) nsRequestInfo(key);
126 return PR_TRUE;
129 static void
130 RequestInfoHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
132 nsRequestInfo* info = static_cast<nsRequestInfo *>(entry);
133 info->~nsRequestInfo();
136 struct nsListenerInfo {
137 nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask)
138 : mWeakListener(aListener),
139 mNotifyMask(aNotifyMask)
143 // Weak pointer for the nsIWebProgressListener...
144 nsWeakPtr mWeakListener;
146 // Mask indicating which notifications the listener wants to receive.
147 unsigned long mNotifyMask;
151 nsDocLoader::nsDocLoader()
152 : mParent(nsnull),
153 mListenerInfoList(8),
154 mIsLoadingDocument(PR_FALSE),
155 mIsRestoringDocument(PR_FALSE),
156 mDontFlushLayout(PR_FALSE),
157 mIsFlushingLayout(PR_FALSE)
159 #if defined(PR_LOGGING)
160 if (nsnull == gDocLoaderLog) {
161 gDocLoaderLog = PR_NewLogModule("DocLoader");
163 #endif /* PR_LOGGING */
165 static PLDHashTableOps hash_table_ops =
167 PL_DHashAllocTable,
168 PL_DHashFreeTable,
169 PL_DHashVoidPtrKeyStub,
170 PL_DHashMatchEntryStub,
171 PL_DHashMoveEntryStub,
172 RequestInfoHashClearEntry,
173 PL_DHashFinalizeStub,
174 RequestInfoHashInitEntry
177 if (!PL_DHashTableInit(&mRequestInfoHash, &hash_table_ops, nsnull,
178 sizeof(nsRequestInfo), 16)) {
179 mRequestInfoHash.ops = nsnull;
182 ClearInternalProgress();
184 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
185 ("DocLoader:%p: created.\n", this));
188 nsresult
189 nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
191 mParent = aParent;
192 return NS_OK;
195 nsresult
196 nsDocLoader::Init()
198 if (!mRequestInfoHash.ops) {
199 return NS_ERROR_OUT_OF_MEMORY;
202 nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
203 if (NS_FAILED(rv)) return rv;
205 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
206 ("DocLoader:%p: load group %x.\n", this, mLoadGroup.get()));
208 return NS_OK;
211 nsDocLoader::~nsDocLoader()
214 |ClearWeakReferences()| here is intended to prevent people holding weak references
215 from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
216 subsequent |Release()| will try to destroy me. At this point there should be only
217 weak references remaining (otherwise, we wouldn't be getting destroyed).
219 An alternative would be incrementing our refcount (consider it a compressed flag
220 saying "Don't re-destroy."). I haven't yet decided which is better. [scc]
222 // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
223 // this needed?
224 ClearWeakReferences();
226 Destroy();
228 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
229 ("DocLoader:%p: deleted.\n", this));
231 if (mRequestInfoHash.ops) {
232 PL_DHashTableFinish(&mRequestInfoHash);
238 * Implementation of ISupports methods...
240 NS_IMPL_THREADSAFE_ADDREF(nsDocLoader)
241 NS_IMPL_THREADSAFE_RELEASE(nsDocLoader)
243 NS_INTERFACE_MAP_BEGIN(nsDocLoader)
244 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
245 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
246 NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
247 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
248 NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
249 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
250 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
251 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
252 NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
253 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
254 if (aIID.Equals(kThisImplCID))
255 foundInterface = static_cast<nsIDocumentLoader *>(this);
256 else
257 NS_INTERFACE_MAP_END
261 * Implementation of nsIInterfaceRequestor methods...
263 NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
265 nsresult rv = NS_ERROR_NO_INTERFACE;
267 NS_ENSURE_ARG_POINTER(aSink);
269 if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
270 *aSink = mLoadGroup;
271 NS_IF_ADDREF((nsISupports*)*aSink);
272 rv = NS_OK;
273 } else {
274 rv = QueryInterface(aIID, aSink);
277 return rv;
280 /* static */
281 already_AddRefed<nsDocLoader>
282 nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
284 if (!aSupports) {
285 return nsnull;
288 nsDocLoader* ptr;
289 CallQueryInterface(aSupports, &ptr);
290 return ptr;
293 /* static */
294 nsresult
295 nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
297 nsresult rv;
298 nsCOMPtr<nsIDocumentLoader> docLoaderService =
299 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
300 NS_ENSURE_SUCCESS(rv, rv);
302 nsRefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
303 NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
305 return rootDocLoader->AddChildLoader(aDocLoader);
308 NS_IMETHODIMP
309 nsDocLoader::Stop(void)
311 nsresult rv = NS_OK;
312 PRInt32 count, i;
314 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
315 ("DocLoader:%p: Stop() called\n", this));
317 count = mChildList.Count();
319 nsCOMPtr<nsIDocumentLoader> loader;
320 for (i=0; i < count; i++) {
321 loader = ChildAt(i);
323 if (loader) {
324 (void) loader->Stop();
328 if (mLoadGroup)
329 rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
331 // Don't report that we're flushing layout so IsBusy returns false after a
332 // Stop call.
333 mIsFlushingLayout = PR_FALSE;
335 // Clear out mChildrenInOnload. We want to make sure to fire our
336 // onload at this point, and there's no issue with mChildrenInOnload
337 // after this, since mDocumentRequest will be null after the
338 // DocLoaderIsEmpty() call.
339 mChildrenInOnload.Clear();
341 // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
342 // etc, as needed. We could be getting into here from a subframe onload, in
343 // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
344 // happened yet, Canceling the loadgroup did nothing (because it was already
345 // empty), and we're about to start a new load (which is what triggered this
346 // Stop() call).
348 // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
349 // we wouldn't need the call here....
351 NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
352 DocLoaderIsEmpty(PR_FALSE);
354 return rv;
358 PRBool
359 nsDocLoader::IsBusy()
361 nsresult rv;
364 // A document loader is busy if either:
366 // 1. One of its children is in the middle of an onload handler. Note that
367 // the handler may have already removed this child from mChildList!
368 // 2. It is currently loading a document and either has parts of it still
369 // loading, or has a busy child docloader.
370 // 3. It's currently flushing layout in DocLoaderIsEmpty().
373 if (mChildrenInOnload.Count() || mIsFlushingLayout) {
374 return PR_TRUE;
377 /* Is this document loader busy? */
378 if (!mIsLoadingDocument) {
379 return PR_FALSE;
382 PRBool busy;
383 rv = mLoadGroup->IsPending(&busy);
384 if (NS_FAILED(rv)) {
385 return PR_FALSE;
387 if (busy) {
388 return PR_TRUE;
391 /* check its child document loaders... */
392 PRInt32 count, i;
394 count = mChildList.Count();
396 for (i=0; i < count; i++) {
397 nsIDocumentLoader* loader = ChildAt(i);
399 // This is a safe cast, because we only put nsDocLoader objects into the
400 // array
401 if (loader && static_cast<nsDocLoader*>(loader)->IsBusy())
402 return PR_TRUE;
405 return PR_FALSE;
408 NS_IMETHODIMP
409 nsDocLoader::GetContainer(nsISupports** aResult)
411 NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
413 return NS_OK;
416 NS_IMETHODIMP
417 nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
419 nsresult rv = NS_OK;
421 if (nsnull == aResult) {
422 rv = NS_ERROR_NULL_POINTER;
423 } else {
424 *aResult = mLoadGroup;
425 NS_IF_ADDREF(*aResult);
427 return rv;
430 void
431 nsDocLoader::Destroy()
433 Stop();
435 // Remove the document loader from the parent list of loaders...
436 if (mParent)
438 mParent->RemoveChildLoader(this);
441 // Release all the information about network requests...
442 ClearRequestInfoHash();
444 // Release all the information about registered listeners...
445 PRInt32 count = mListenerInfoList.Count();
446 for(PRInt32 i = 0; i < count; i++) {
447 nsListenerInfo *info =
448 static_cast<nsListenerInfo*>(mListenerInfoList.ElementAt(i));
450 delete info;
453 mListenerInfoList.Clear();
454 mListenerInfoList.Compact();
456 mDocumentRequest = 0;
458 if (mLoadGroup)
459 mLoadGroup->SetGroupObserver(nsnull);
461 DestroyChildren();
464 void
465 nsDocLoader::DestroyChildren()
467 PRInt32 i, count;
469 count = mChildList.Count();
470 // if the doc loader still has children...we need to enumerate the
471 // children and make them null out their back ptr to the parent doc
472 // loader
473 for (i=0; i < count; i++)
475 nsIDocumentLoader* loader = ChildAt(i);
477 if (loader) {
478 // This is a safe cast, as we only put nsDocLoader objects into the
479 // array
480 static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nsnull);
483 mChildList.Clear();
486 NS_IMETHODIMP
487 nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
489 // called each time a request is added to the group.
491 #ifdef PR_LOGGING
492 if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
493 nsCAutoString name;
494 request->GetName(name);
496 PRUint32 count = 0;
497 if (mLoadGroup)
498 mLoadGroup->GetActiveCount(&count);
500 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
501 ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
502 this, request, name.get(),
503 (mIsLoadingDocument ? "true" : "false"),
504 count));
506 #endif /* PR_LOGGING */
507 PRBool bJustStartedLoading = PR_FALSE;
509 nsLoadFlags loadFlags = 0;
510 request->GetLoadFlags(&loadFlags);
512 if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
513 bJustStartedLoading = PR_TRUE;
514 mIsLoadingDocument = PR_TRUE;
515 ClearInternalProgress(); // only clear our progress if we are starting a new load....
519 // Create a new nsRequestInfo for the request that is starting to
520 // load...
522 AddRequestInfo(request);
525 // Only fire a doStartDocumentLoad(...) if the document loader
526 // has initiated a load... Otherwise, this notification has
527 // resulted from a request being added to the load group.
529 if (mIsLoadingDocument) {
530 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
532 // Make sure that the document channel is null at this point...
533 // (unless its been redirected)
535 NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
536 !(mDocumentRequest.get()),
537 "Overwriting an existing document channel!");
539 // This request is associated with the entire document...
540 mDocumentRequest = request;
541 mLoadGroup->SetDefaultLoadRequest(request);
543 // Only fire the start document load notification for the first
544 // document URI... Do not fire it again for redirections
546 if (bJustStartedLoading) {
547 // Update the progress status state
548 mProgressStateFlags = nsIWebProgressListener::STATE_START;
550 // Fire the start document load notification
551 doStartDocumentLoad();
552 return NS_OK;
557 NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
558 "mDocumentRequest MUST be set for the duration of a page load!");
560 doStartURLLoad(request);
562 return NS_OK;
565 NS_IMETHODIMP
566 nsDocLoader::OnStopRequest(nsIRequest *aRequest,
567 nsISupports *aCtxt,
568 nsresult aStatus)
570 nsresult rv = NS_OK;
572 #ifdef PR_LOGGING
573 if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
574 nsCAutoString name;
575 aRequest->GetName(name);
577 PRUint32 count = 0;
578 if (mLoadGroup)
579 mLoadGroup->GetActiveCount(&count);
581 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
582 ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
583 this, aRequest, name.get(),
584 aStatus, (mIsLoadingDocument ? "true" : "false"),
585 count));
587 #endif
589 PRBool bFireTransferring = PR_FALSE;
592 // Set the Maximum progress to the same value as the current progress.
593 // Since the URI has finished loading, all the data is there. Also,
594 // this will allow a more accurate estimation of the max progress (in case
595 // the old value was unknown ie. -1)
597 nsRequestInfo *info = GetRequestInfo(aRequest);
598 if (info) {
599 info->mIsDone = PR_TRUE;
601 nsInt64 oldMax = info->mMaxProgress;
603 info->mMaxProgress = info->mCurrentProgress;
606 // If a request whose content-length was previously unknown has just
607 // finished loading, then use this new data to try to calculate a
608 // mMaxSelfProgress...
610 if ((oldMax < nsInt64(0)) && (mMaxSelfProgress < nsInt64(0))) {
611 mMaxSelfProgress = CalculateMaxProgress();
614 // As we know the total progress of this request now, save it to be part
615 // of CalculateMaxProgress() result. We need to remove the info from the
616 // hash, see bug 480713.
617 mCompletedTotalProgress += info->mMaxProgress;
620 // Determine whether a STATE_TRANSFERRING notification should be
621 // 'synthesized'.
623 // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
624 // nsRequestInfo::mCurrentProgress are both 0, then the
625 // STATE_TRANSFERRING notification has not been fired yet...
627 if ((oldMax == LL_ZERO) && (info->mCurrentProgress == LL_ZERO)) {
628 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
630 // Only fire a TRANSFERRING notification if the request is also a
631 // channel -- data transfer requires a nsIChannel!
633 if (channel) {
634 if (NS_SUCCEEDED(aStatus)) {
635 bFireTransferring = PR_TRUE;
638 // If the request failed (for any reason other than being
639 // redirected or retargeted), the TRANSFERRING notification can
640 // still be fired if a HTTP connection was established to a server.
642 else if (aStatus != NS_BINDING_REDIRECTED &&
643 aStatus != NS_BINDING_RETARGETED) {
645 // Only if the load has been targeted (see bug 268483)...
647 PRUint32 lf;
648 channel->GetLoadFlags(&lf);
649 if (lf & nsIChannel::LOAD_TARGETED) {
650 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
651 if (httpChannel) {
652 PRUint32 responseCode;
653 rv = httpChannel->GetResponseStatus(&responseCode);
654 if (NS_SUCCEEDED(rv)) {
656 // A valid server status indicates that a connection was
657 // established to the server... So, fire the notification
658 // even though a failure occurred later...
660 bFireTransferring = PR_TRUE;
669 if (bFireTransferring) {
670 // Send a STATE_TRANSFERRING notification for the request.
671 PRInt32 flags;
673 flags = nsIWebProgressListener::STATE_TRANSFERRING |
674 nsIWebProgressListener::STATE_IS_REQUEST;
676 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
678 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
679 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
681 // Send STATE_TRANSFERRING for the document too...
682 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
685 FireOnStateChange(this, aRequest, flags, NS_OK);
689 // Fire the OnStateChange(...) notification for stop request
691 doStopURLLoad(aRequest, aStatus);
693 // Clear this request out of the hash to avoid bypass of FireOnStateChange
694 // when address of the request is reused.
695 RemoveRequestInfo(aRequest);
698 // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
699 // load. This will handle removing the request from our hashtable as needed.
701 if (mIsLoadingDocument) {
702 DocLoaderIsEmpty(PR_TRUE);
705 return NS_OK;
709 nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
711 nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
712 if (NS_SUCCEEDED(rv)) {
713 aChild->SetDocLoaderParent(nsnull);
715 return rv;
718 nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
720 nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
721 if (NS_SUCCEEDED(rv)) {
722 aChild->SetDocLoaderParent(this);
724 return rv;
727 NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
729 if (!mDocumentRequest) {
730 *aChannel = nsnull;
731 return NS_OK;
734 return CallQueryInterface(mDocumentRequest, aChannel);
738 void nsDocLoader::DocLoaderIsEmpty(PRBool aFlushLayout)
740 if (mIsLoadingDocument) {
741 /* In the unimagineably rude circumstance that onload event handlers
742 triggered by this function actually kill the window ... ok, it's
743 not unimagineable; it's happened ... this deathgrip keeps this object
744 alive long enough to survive this function call. */
745 nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
747 // Don't flush layout if we're still busy.
748 if (IsBusy()) {
749 return;
752 NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
754 // The load group for this DocumentLoader is idle. Flush if we need to.
755 if (aFlushLayout && !mDontFlushLayout) {
756 nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(GetAsSupports(this));
757 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
758 if (doc) {
759 // We start loads from style resolution, so we need to flush out style
760 // no matter what. If we have user fonts, we also need to flush layout,
761 // since the reflow is what starts font loads.
762 mozFlushType flushType = Flush_Style;
763 nsIPresShell* shell = doc->GetShell();
764 if (shell) {
765 // Be safe in case this presshell is in teardown now
766 nsPresContext* presContext = shell->GetPresContext();
767 if (presContext && presContext->GetUserFontSet()) {
768 flushType = Flush_Layout;
771 mDontFlushLayout = mIsFlushingLayout = PR_TRUE;
772 doc->FlushPendingNotifications(flushType);
773 mDontFlushLayout = mIsFlushingLayout = PR_FALSE;
777 // And now check whether we're really busy; that might have changed with
778 // the layout flush.
779 if (!IsBusy()) {
780 // Clear out our request info hash, now that our load really is done and
781 // we don't need it anymore to CalculateMaxProgress().
782 ClearInternalProgress();
784 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
785 ("DocLoader:%p: Is now idle...\n", this));
787 nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
789 NS_ASSERTION(mDocumentRequest, "No Document Request!");
790 mDocumentRequest = 0;
791 mIsLoadingDocument = PR_FALSE;
793 // Update the progress status state - the document is done
794 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
797 nsresult loadGroupStatus = NS_OK;
798 mLoadGroup->GetStatus(&loadGroupStatus);
801 // New code to break the circular reference between
802 // the load group and the docloader...
804 mLoadGroup->SetDefaultLoadRequest(nsnull);
806 // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
807 // it even if our onload handler removes us from the docloader tree.
808 nsRefPtr<nsDocLoader> parent = mParent;
810 // Note that if calling ChildEnteringOnload() on the parent returns false
811 // then calling our onload handler is not safe. That can only happen on
812 // OOM, so that's ok.
813 if (!parent || parent->ChildEnteringOnload(this)) {
814 // Do nothing with our state after firing the
815 // OnEndDocumentLoad(...). The document loader may be loading a *new*
816 // document - if LoadDocument() was called from a handler!
818 doStopDocumentLoad(docRequest, loadGroupStatus);
820 if (parent) {
821 parent->ChildDoneWithOnload(this);
828 void nsDocLoader::doStartDocumentLoad(void)
831 #if defined(DEBUG)
832 nsCAutoString buffer;
834 GetURIStringFromRequest(mDocumentRequest, buffer);
835 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
836 ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
837 "\tURI: %s \n",
838 this, buffer.get()));
839 #endif /* DEBUG */
841 // Fire an OnStatus(...) notification STATE_START. This indicates
842 // that the document represented by mDocumentRequest has started to
843 // load...
844 FireOnStateChange(this,
845 mDocumentRequest,
846 nsIWebProgressListener::STATE_START |
847 nsIWebProgressListener::STATE_IS_DOCUMENT |
848 nsIWebProgressListener::STATE_IS_REQUEST |
849 nsIWebProgressListener::STATE_IS_WINDOW |
850 nsIWebProgressListener::STATE_IS_NETWORK,
851 NS_OK);
854 void nsDocLoader::doStartURLLoad(nsIRequest *request)
856 #if defined(DEBUG)
857 nsCAutoString buffer;
859 GetURIStringFromRequest(request, buffer);
860 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
861 ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
862 "\tURI: %s\n",
863 this, buffer.get()));
864 #endif /* DEBUG */
866 FireOnStateChange(this,
867 request,
868 nsIWebProgressListener::STATE_START |
869 nsIWebProgressListener::STATE_IS_REQUEST,
870 NS_OK);
873 // PLDHashTable enumeration callback that finds a RequestInfo that's not done
874 // yet.
875 static PLDHashOperator
876 FindUnfinishedRequestCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
877 PRUint32 number, void *arg)
879 nsRequestInfo* info = static_cast<nsRequestInfo *>(hdr);
880 nsRequestInfo** retval = static_cast<nsRequestInfo**>(arg);
882 if (!info->mIsDone && !info->mLastStatus.IsEmpty()) {
883 *retval = info;
884 return PL_DHASH_STOP;
887 return PL_DHASH_NEXT;
891 void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
893 #if defined(DEBUG)
894 nsCAutoString buffer;
896 GetURIStringFromRequest(request, buffer);
897 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
898 ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
899 "\tURI: %s status=%x\n",
900 this, buffer.get(), aStatus));
901 #endif /* DEBUG */
903 FireOnStateChange(this,
904 request,
905 nsIWebProgressListener::STATE_STOP |
906 nsIWebProgressListener::STATE_IS_REQUEST,
907 aStatus);
909 // Fire a status change message for a random unfinished request to make sure
910 // that the displayed status is not outdated.
911 nsRequestInfo* unfinishedRequest = nsnull;
912 PL_DHashTableEnumerate(&mRequestInfoHash, FindUnfinishedRequestCallback,
913 &unfinishedRequest);
914 if (unfinishedRequest) {
915 FireOnStatusChange(this, unfinishedRequest->Request(),
916 unfinishedRequest->mLastStatusCode,
917 unfinishedRequest->mLastStatus.get());
921 void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
922 nsresult aStatus)
924 #if defined(DEBUG)
925 nsCAutoString buffer;
927 GetURIStringFromRequest(request, buffer);
928 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
929 ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
930 "\tURI: %s Status=%x\n",
931 this, buffer.get(), aStatus));
932 #endif /* DEBUG */
935 // Fire an OnStateChange(...) notification indicating the the
936 // current document has finished loading...
938 FireOnStateChange(this,
939 request,
940 nsIWebProgressListener::STATE_STOP |
941 nsIWebProgressListener::STATE_IS_DOCUMENT,
942 aStatus);
945 // Fire a final OnStateChange(...) notification indicating the the
946 // current document has finished loading...
948 FireOnStateChange(this,
949 request,
950 nsIWebProgressListener::STATE_STOP |
951 nsIWebProgressListener::STATE_IS_WINDOW |
952 nsIWebProgressListener::STATE_IS_NETWORK,
953 aStatus);
956 ////////////////////////////////////////////////////////////////////////////////////
957 // The following section contains support for nsIWebProgress and related stuff
958 ////////////////////////////////////////////////////////////////////////////////////
960 NS_IMETHODIMP
961 nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
962 PRUint32 aNotifyMask)
964 nsresult rv;
966 nsListenerInfo* info = GetListenerInfo(aListener);
967 if (info) {
968 // The listener is already registered!
969 return NS_ERROR_FAILURE;
972 nsWeakPtr listener = do_GetWeakReference(aListener);
973 if (!listener) {
974 return NS_ERROR_INVALID_ARG;
977 info = new nsListenerInfo(listener, aNotifyMask);
978 if (!info) {
979 return NS_ERROR_OUT_OF_MEMORY;
982 rv = mListenerInfoList.AppendElement(info) ? NS_OK : NS_ERROR_FAILURE;
983 return rv;
986 NS_IMETHODIMP
987 nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
989 nsresult rv;
991 nsListenerInfo* info = GetListenerInfo(aListener);
992 if (info) {
993 rv = mListenerInfoList.RemoveElement(info) ? NS_OK : NS_ERROR_FAILURE;
994 delete info;
995 } else {
996 // The listener is not registered!
997 rv = NS_ERROR_FAILURE;
999 return rv;
1002 NS_IMETHODIMP
1003 nsDocLoader::GetDOMWindow(nsIDOMWindow **aResult)
1005 return CallGetInterface(this, aResult);
1008 NS_IMETHODIMP
1009 nsDocLoader::GetIsLoadingDocument(PRBool *aIsLoadingDocument)
1011 *aIsLoadingDocument = mIsLoadingDocument;
1013 return NS_OK;
1016 PRInt64 nsDocLoader::GetMaxTotalProgress()
1018 nsInt64 newMaxTotal = 0;
1020 PRInt32 count = mChildList.Count();
1021 nsCOMPtr<nsIWebProgress> webProgress;
1022 for (PRInt32 i=0; i < count; i++)
1024 nsInt64 individualProgress = 0;
1025 nsIDocumentLoader* docloader = ChildAt(i);
1026 if (docloader)
1028 // Cast is safe since all children are nsDocLoader too
1029 individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
1031 if (individualProgress < nsInt64(0)) // if one of the elements doesn't know it's size
1032 // then none of them do
1034 newMaxTotal = nsInt64(-1);
1035 break;
1037 else
1038 newMaxTotal += individualProgress;
1041 nsInt64 progress = -1;
1042 if (mMaxSelfProgress >= nsInt64(0) && newMaxTotal >= nsInt64(0))
1043 progress = newMaxTotal + mMaxSelfProgress;
1045 return progress;
1048 ////////////////////////////////////////////////////////////////////////////////////
1049 // The following section contains support for nsIProgressEventSink which is used to
1050 // pass progress and status between the actual request and the doc loader. The doc loader
1051 // then turns around and makes the right web progress calls based on this information.
1052 ////////////////////////////////////////////////////////////////////////////////////
1054 NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt,
1055 PRUint64 aProgress, PRUint64 aProgressMax)
1057 nsRequestInfo *info;
1058 nsInt64 progressDelta = 0;
1061 // Update the RequestInfo entry with the new progress data
1063 info = GetRequestInfo(aRequest);
1064 if (info) {
1065 // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
1066 if (!info->mUploading && (nsInt64(0) == info->mCurrentProgress) && (nsInt64(0) == info->mMaxProgress)) {
1068 // If we receive an OnProgress event from a toplevel channel that the URI Loader
1069 // has not yet targeted, then we must suppress the event. This is necessary to
1070 // ensure that webprogresslisteners do not get confused when the channel is
1071 // finally targeted. See bug 257308.
1073 nsLoadFlags lf = 0;
1074 aRequest->GetLoadFlags(&lf);
1075 if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
1076 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1077 ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
1078 return NS_OK;
1082 // This is the first progress notification for the entry. If
1083 // (aMaxProgress > 0) then the content-length of the data is known,
1084 // so update mMaxSelfProgress... Otherwise, set it to -1 to indicate
1085 // that the content-length is no longer known.
1087 if (PRUint64(aProgressMax) != LL_MAXUINT) {
1088 mMaxSelfProgress += PRInt64(aProgressMax);
1089 info->mMaxProgress = PRInt64(aProgressMax);
1090 } else {
1091 mMaxSelfProgress = nsInt64(-1);
1092 info->mMaxProgress = nsInt64(-1);
1095 // Send a STATE_TRANSFERRING notification for the request.
1096 PRInt32 flags;
1098 flags = nsIWebProgressListener::STATE_TRANSFERRING |
1099 nsIWebProgressListener::STATE_IS_REQUEST;
1101 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
1103 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
1104 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
1106 // Send STATE_TRANSFERRING for the document too...
1107 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1110 FireOnStateChange(this, aRequest, flags, NS_OK);
1113 // Update the current progress count...
1114 progressDelta = nsInt64(PRInt64(aProgress)) - info->mCurrentProgress;
1115 mCurrentSelfProgress += progressDelta;
1117 info->mCurrentProgress = PRInt64(aProgress);
1120 // The request is not part of the load group, so ignore its progress
1121 // information...
1123 else {
1124 #if defined(DEBUG)
1125 nsCAutoString buffer;
1127 GetURIStringFromRequest(aRequest, buffer);
1128 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1129 ("DocLoader:%p OOPS - No Request Info for: %s\n",
1130 this, buffer.get()));
1131 #endif /* DEBUG */
1133 return NS_OK;
1137 // Fire progress notifications out to any registered nsIWebProgressListeners
1139 FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
1140 mCurrentTotalProgress, mMaxTotalProgress);
1142 return NS_OK;
1145 NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt,
1146 nsresult aStatus, const PRUnichar* aStatusArg)
1149 // Fire progress notifications out to any registered nsIWebProgressListeners
1151 if (aStatus) {
1152 // Remember the current status for this request
1153 nsRequestInfo *info;
1154 info = GetRequestInfo(aRequest);
1155 if (info) {
1156 PRBool uploading = (aStatus == nsITransport::STATUS_WRITING ||
1157 aStatus == nsISocketTransport::STATUS_SENDING_TO);
1158 // If switching from uploading to downloading (or vice versa), then we
1159 // need to reset our progress counts. This is designed with HTTP form
1160 // submission in mind, where an upload is performed followed by download
1161 // of possibly several documents.
1162 if (info->mUploading != uploading) {
1163 mCurrentSelfProgress = mMaxSelfProgress = LL_ZERO;
1164 mCurrentTotalProgress = mMaxTotalProgress = LL_ZERO;
1165 mCompletedTotalProgress = LL_ZERO;
1166 info->mUploading = uploading;
1167 info->mCurrentProgress = LL_ZERO;
1168 info->mMaxProgress = LL_ZERO;
1172 nsCOMPtr<nsIStringBundleService> sbs =
1173 mozilla::services::GetStringBundleService();
1174 if (!sbs)
1175 return NS_ERROR_FAILURE;
1176 nsXPIDLString msg;
1177 nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg,
1178 getter_Copies(msg));
1179 if (NS_FAILED(rv))
1180 return rv;
1182 // Keep around the message. In case a request finishes, we need to make sure
1183 // to send the status message of another request to our user to that we
1184 // don't display, for example, "Transferring" messages for requests that are
1185 // already done.
1186 if (info) {
1187 info->mLastStatus = msg;
1188 info->mLastStatusCode = aStatus;
1190 FireOnStatusChange(this, aRequest, aStatus, msg);
1192 return NS_OK;
1195 void nsDocLoader::ClearInternalProgress()
1197 ClearRequestInfoHash();
1199 mCurrentSelfProgress = mMaxSelfProgress = LL_ZERO;
1200 mCurrentTotalProgress = mMaxTotalProgress = LL_ZERO;
1201 mCompletedTotalProgress = LL_ZERO;
1203 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
1207 void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
1208 nsIRequest *request,
1209 PRInt64 aProgress,
1210 PRInt64 aProgressMax,
1211 PRInt64 aProgressDelta,
1212 PRInt64 aTotalProgress,
1213 PRInt64 aMaxTotalProgress)
1215 if (mIsLoadingDocument) {
1216 mCurrentTotalProgress += aProgressDelta;
1217 mMaxTotalProgress = GetMaxTotalProgress();
1219 aTotalProgress = mCurrentTotalProgress;
1220 aMaxTotalProgress = mMaxTotalProgress;
1223 #if defined(DEBUG)
1224 nsCAutoString buffer;
1226 GetURIStringFromRequest(request, buffer);
1227 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1228 ("DocLoader:%p: Progress (%s): curSelf: %d maxSelf: %d curTotal: %d maxTotal %d\n",
1229 this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
1230 #endif /* DEBUG */
1233 * First notify any listeners of the new progress info...
1235 * Operate the elements from back to front so that if items get
1236 * get removed from the list it won't affect our iteration
1238 nsCOMPtr<nsIWebProgressListener> listener;
1239 PRInt32 count = mListenerInfoList.Count();
1241 while (--count >= 0) {
1242 nsListenerInfo *info;
1244 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1245 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_PROGRESS)) {
1246 continue;
1249 listener = do_QueryReferent(info->mWeakListener);
1250 if (!listener) {
1251 // the listener went away. gracefully pull it out of the list.
1252 mListenerInfoList.RemoveElementAt(count);
1253 delete info;
1254 continue;
1257 // XXX truncates 64-bit to 32-bit
1258 listener->OnProgressChange(aLoadInitiator,request,
1259 PRInt32(aProgress), PRInt32(aProgressMax),
1260 PRInt32(aTotalProgress), PRInt32(aMaxTotalProgress));
1263 mListenerInfoList.Compact();
1265 // Pass the notification up to the parent...
1266 if (mParent) {
1267 mParent->FireOnProgressChange(aLoadInitiator, request,
1268 aProgress, aProgressMax,
1269 aProgressDelta,
1270 aTotalProgress, aMaxTotalProgress);
1275 void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
1276 nsIRequest *aRequest,
1277 PRInt32 aStateFlags,
1278 nsresult aStatus)
1281 // Remove the STATE_IS_NETWORK bit if necessary.
1283 // The rule is to remove this bit, if the notification has been passed
1284 // up from a child WebProgress, and the current WebProgress is already
1285 // active...
1287 if (mIsLoadingDocument &&
1288 (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) &&
1289 (this != aProgress)) {
1290 aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
1293 // Add the STATE_RESTORING bit if necessary.
1294 if (mIsRestoringDocument)
1295 aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
1297 #if defined(DEBUG)
1298 nsCAutoString buffer;
1300 GetURIStringFromRequest(aRequest, buffer);
1301 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1302 ("DocLoader:%p: Status (%s): code: %x\n",
1303 this, buffer.get(), aStateFlags));
1304 #endif /* DEBUG */
1306 NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
1309 * First notify any listeners of the new state info...
1311 * Operate the elements from back to front so that if items get
1312 * get removed from the list it won't affect our iteration
1314 nsCOMPtr<nsIWebProgressListener> listener;
1315 PRInt32 count = mListenerInfoList.Count();
1316 PRInt32 notifyMask = (aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL;
1318 while (--count >= 0) {
1319 nsListenerInfo *info;
1321 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1322 if (!info || !(info->mNotifyMask & notifyMask)) {
1323 continue;
1326 listener = do_QueryReferent(info->mWeakListener);
1327 if (!listener) {
1328 // the listener went away. gracefully pull it out of the list.
1329 mListenerInfoList.RemoveElementAt(count);
1330 delete info;
1331 continue;
1334 listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
1337 mListenerInfoList.Compact();
1339 // Pass the notification up to the parent...
1340 if (mParent) {
1341 mParent->FireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
1347 void
1348 nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
1349 nsIRequest* aRequest,
1350 nsIURI *aUri)
1353 * First notify any listeners of the new state info...
1355 * Operate the elements from back to front so that if items get
1356 * get removed from the list it won't affect our iteration
1358 nsCOMPtr<nsIWebProgressListener> listener;
1359 PRInt32 count = mListenerInfoList.Count();
1361 while (--count >= 0) {
1362 nsListenerInfo *info;
1364 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1365 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_LOCATION)) {
1366 continue;
1369 listener = do_QueryReferent(info->mWeakListener);
1370 if (!listener) {
1371 // the listener went away. gracefully pull it out of the list.
1372 mListenerInfoList.RemoveElementAt(count);
1373 delete info;
1374 continue;
1377 listener->OnLocationChange(aWebProgress, aRequest, aUri);
1380 mListenerInfoList.Compact();
1382 // Pass the notification up to the parent...
1383 if (mParent) {
1384 mParent->FireOnLocationChange(aWebProgress, aRequest, aUri);
1388 void
1389 nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
1390 nsIRequest* aRequest,
1391 nsresult aStatus,
1392 const PRUnichar* aMessage)
1395 * First notify any listeners of the new state info...
1397 * Operate the elements from back to front so that if items get
1398 * get removed from the list it won't affect our iteration
1400 nsCOMPtr<nsIWebProgressListener> listener;
1401 PRInt32 count = mListenerInfoList.Count();
1403 while (--count >= 0) {
1404 nsListenerInfo *info;
1406 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1407 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_STATUS)) {
1408 continue;
1411 listener = do_QueryReferent(info->mWeakListener);
1412 if (!listener) {
1413 // the listener went away. gracefully pull it out of the list.
1414 mListenerInfoList.RemoveElementAt(count);
1415 delete info;
1416 continue;
1419 listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
1421 mListenerInfoList.Compact();
1423 // Pass the notification up to the parent...
1424 if (mParent) {
1425 mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
1429 PRBool
1430 nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress,
1431 nsIURI *aURI,
1432 PRInt32 aDelay,
1433 PRBool aSameURI)
1436 * Returns true if the refresh may proceed,
1437 * false if the refresh should be blocked.
1439 * First notify any listeners of the refresh attempt...
1441 * Iterate the elements from back to front so that if items
1442 * get removed from the list it won't affect our iteration
1444 PRBool allowRefresh = PR_TRUE;
1445 PRInt32 count = mListenerInfoList.Count();
1447 while (--count >= 0) {
1448 nsListenerInfo *info;
1450 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1451 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_REFRESH)) {
1452 continue;
1455 nsCOMPtr<nsIWebProgressListener> listener =
1456 do_QueryReferent(info->mWeakListener);
1457 if (!listener) {
1458 // the listener went away. gracefully pull it out of the list.
1459 mListenerInfoList.RemoveElementAt(count);
1460 delete info;
1461 continue;
1464 nsCOMPtr<nsIWebProgressListener2> listener2 =
1465 do_QueryReferent(info->mWeakListener);
1466 if (!listener2)
1467 continue;
1469 PRBool listenerAllowedRefresh;
1470 nsresult listenerRV = listener2->OnRefreshAttempted(
1471 aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
1472 if (NS_FAILED(listenerRV))
1473 continue;
1475 allowRefresh = allowRefresh && listenerAllowedRefresh;
1478 mListenerInfoList.Compact();
1480 // Pass the notification up to the parent...
1481 if (mParent) {
1482 allowRefresh = allowRefresh &&
1483 mParent->RefreshAttempted(aWebProgress, aURI, aDelay, aSameURI);
1486 return allowRefresh;
1489 nsListenerInfo *
1490 nsDocLoader::GetListenerInfo(nsIWebProgressListener *aListener)
1492 PRInt32 i, count;
1493 nsListenerInfo *info;
1495 nsCOMPtr<nsISupports> listener1 = do_QueryInterface(aListener);
1496 count = mListenerInfoList.Count();
1497 for (i=0; i<count; i++) {
1498 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(i));
1500 NS_ASSERTION(info, "There should NEVER be a null listener in the list");
1501 if (info) {
1502 nsCOMPtr<nsISupports> listener2 = do_QueryReferent(info->mWeakListener);
1503 if (listener1 == listener2)
1504 return info;
1507 return nsnull;
1510 nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
1512 if (!PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_ADD)) {
1513 return NS_ERROR_OUT_OF_MEMORY;
1516 return NS_OK;
1519 void nsDocLoader::RemoveRequestInfo(nsIRequest *aRequest)
1521 PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_REMOVE);
1524 nsRequestInfo * nsDocLoader::GetRequestInfo(nsIRequest *aRequest)
1526 nsRequestInfo *info =
1527 static_cast<nsRequestInfo *>
1528 (PL_DHashTableOperate(&mRequestInfoHash, aRequest,
1529 PL_DHASH_LOOKUP));
1531 if (PL_DHASH_ENTRY_IS_FREE(info)) {
1532 // Nothing found in the hash, return null.
1534 return nsnull;
1537 // Return what we found in the hash...
1539 return info;
1542 // PLDHashTable enumeration callback that just removes every entry
1543 // from the hash.
1544 static PLDHashOperator
1545 RemoveInfoCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number,
1546 void *arg)
1548 return PL_DHASH_REMOVE;
1551 void nsDocLoader::ClearRequestInfoHash(void)
1553 if (!mRequestInfoHash.ops || !mRequestInfoHash.entryCount) {
1554 // No hash, or the hash is empty, nothing to do here then...
1556 return;
1559 PL_DHashTableEnumerate(&mRequestInfoHash, RemoveInfoCallback, nsnull);
1562 // PLDHashTable enumeration callback that calculates the max progress.
1563 static PLDHashOperator
1564 CalcMaxProgressCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
1565 PRUint32 number, void *arg)
1567 const nsRequestInfo *info = static_cast<const nsRequestInfo *>(hdr);
1568 nsInt64 *max = static_cast<nsInt64 *>(arg);
1570 if (info->mMaxProgress < info->mCurrentProgress) {
1571 *max = nsInt64(-1);
1573 return PL_DHASH_STOP;
1576 *max += info->mMaxProgress;
1578 return PL_DHASH_NEXT;
1581 PRInt64 nsDocLoader::CalculateMaxProgress()
1583 nsInt64 max = mCompletedTotalProgress;
1584 PL_DHashTableEnumerate(&mRequestInfoHash, CalcMaxProgressCallback, &max);
1585 return max;
1588 NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
1589 nsIChannel *aNewChannel,
1590 PRUint32 aFlags,
1591 nsIAsyncVerifyRedirectCallback *cb)
1593 if (aOldChannel)
1595 nsLoadFlags loadFlags = 0;
1596 PRInt32 stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
1597 nsIWebProgressListener::STATE_IS_REQUEST;
1599 aOldChannel->GetLoadFlags(&loadFlags);
1600 // If the document channel is being redirected, then indicate that the
1601 // document is being redirected in the notification...
1602 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
1604 stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1606 #if defined(DEBUG)
1607 nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
1608 NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
1609 #endif /* DEBUG */
1612 OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
1613 FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
1616 cb->OnRedirectVerifyCallback(NS_OK);
1617 return NS_OK;
1621 * Implementation of nsISecurityEventSink method...
1624 NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
1625 PRUint32 aState)
1628 // Fire progress notifications out to any registered nsIWebProgressListeners.
1631 nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
1632 nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
1635 * First notify any listeners of the new state info...
1637 * Operate the elements from back to front so that if items get
1638 * get removed from the list it won't affect our iteration
1640 nsCOMPtr<nsIWebProgressListener> listener;
1641 PRInt32 count = mListenerInfoList.Count();
1643 while (--count >= 0) {
1644 nsListenerInfo *info;
1646 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1647 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_SECURITY)) {
1648 continue;
1651 listener = do_QueryReferent(info->mWeakListener);
1652 if (!listener) {
1653 // the listener went away. gracefully pull it out of the list.
1654 mListenerInfoList.RemoveElementAt(count);
1655 delete info;
1656 continue;
1659 listener->OnSecurityChange(webProgress, request, aState);
1662 mListenerInfoList.Compact();
1664 // Pass the notification up to the parent...
1665 if (mParent) {
1666 mParent->OnSecurityChange(aContext, aState);
1668 return NS_OK;
1672 * Implementation of nsISupportsPriority methods...
1674 * The priority of the DocLoader _is_ the priority of its LoadGroup.
1676 * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
1677 * go away.
1680 NS_IMETHODIMP nsDocLoader::GetPriority(PRInt32 *aPriority)
1682 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1683 if (p)
1684 return p->GetPriority(aPriority);
1686 *aPriority = 0;
1687 return NS_OK;
1690 NS_IMETHODIMP nsDocLoader::SetPriority(PRInt32 aPriority)
1692 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1693 ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
1695 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1696 if (p)
1697 p->SetPriority(aPriority);
1699 PRInt32 count = mChildList.Count();
1701 nsDocLoader *loader;
1702 for (PRInt32 i=0; i < count; i++) {
1703 loader = static_cast<nsDocLoader*>(ChildAt(i));
1704 if (loader) {
1705 loader->SetPriority(aPriority);
1709 return NS_OK;
1712 NS_IMETHODIMP nsDocLoader::AdjustPriority(PRInt32 aDelta)
1714 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1715 ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
1717 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1718 if (p)
1719 p->AdjustPriority(aDelta);
1721 PRInt32 count = mChildList.Count();
1723 nsDocLoader *loader;
1724 for (PRInt32 i=0; i < count; i++) {
1725 loader = static_cast<nsDocLoader*>(ChildAt(i));
1726 if (loader) {
1727 loader->AdjustPriority(aDelta);
1731 return NS_OK;
1737 #if 0
1738 void nsDocLoader::DumpChannelInfo()
1740 nsChannelInfo *info;
1741 PRInt32 i, count;
1742 PRInt32 current=0, max=0;
1745 printf("==== DocLoader=%x\n", this);
1747 count = mChannelInfoList.Count();
1748 for(i=0; i<count; i++) {
1749 info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
1751 #if defined(DEBUG)
1752 nsCAutoString buffer;
1753 nsresult rv = NS_OK;
1754 if (info->mURI) {
1755 rv = info->mURI->GetSpec(buffer);
1758 printf(" [%d] current=%d max=%d [%s]\n", i,
1759 info->mCurrentProgress,
1760 info->mMaxProgress, buffer.get());
1761 #endif /* DEBUG */
1763 current += info->mCurrentProgress;
1764 if (max >= 0) {
1765 if (info->mMaxProgress < info->mCurrentProgress) {
1766 max = -1;
1767 } else {
1768 max += info->mMaxProgress;
1773 printf("\nCurrent=%d Total=%d\n====\n", current, max);
1775 #endif /* 0 */