Bug 972623 - Call SetCompositableBackendSpecificData() in ImageHost::Composite()...
[gecko.git] / uriloader / base / nsDocLoader.cpp
blobb57e7d191d9ff18a8eebd41b4e8d290ab4b935dd
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nspr.h"
7 #include "prlog.h"
9 #include "nsDocLoader.h"
10 #include "nsCURILoader.h"
11 #include "nsNetUtil.h"
12 #include "nsIHttpChannel.h"
13 #include "nsIWebProgressListener2.h"
15 #include "nsIServiceManager.h"
16 #include "nsXPIDLString.h"
18 #include "nsIURL.h"
19 #include "nsCOMPtr.h"
20 #include "nscore.h"
21 #include "nsWeakPtr.h"
22 #include "nsAutoPtr.h"
24 #include "nsIDOMWindow.h"
26 #include "nsIStringBundle.h"
27 #include "nsIScriptSecurityManager.h"
29 #include "nsITransport.h"
30 #include "nsISocketTransport.h"
32 #include "nsIDOMDocument.h"
33 #include "nsIDocument.h"
34 #include "nsPresContext.h"
35 #include "nsIAsyncVerifyRedirectCallback.h"
37 static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
39 #if defined(PR_LOGGING)
41 // Log module for nsIDocumentLoader logging...
43 // To enable logging (see prlog.h for full details):
45 // set NSPR_LOG_MODULES=DocLoader:5
46 // set NSPR_LOG_FILE=nspr.log
48 // this enables PR_LOG_DEBUG level information and places all output in
49 // the file nspr.log
51 PRLogModuleInfo* gDocLoaderLog = nullptr;
52 #endif /* PR_LOGGING */
55 #if defined(DEBUG)
56 void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
58 if (request)
59 request->GetName(name);
60 else
61 name.AssignLiteral("???");
63 #endif /* DEBUG */
67 bool
68 nsDocLoader::RequestInfoHashInitEntry(PLDHashTable* table,
69 PLDHashEntryHdr* entry,
70 const void* key)
72 // Initialize the entry with placement new
73 new (entry) nsRequestInfo(key);
74 return true;
77 void
78 nsDocLoader::RequestInfoHashClearEntry(PLDHashTable* table,
79 PLDHashEntryHdr* entry)
81 nsRequestInfo* info = static_cast<nsRequestInfo *>(entry);
82 info->~nsRequestInfo();
85 struct nsListenerInfo {
86 nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask)
87 : mWeakListener(aListener),
88 mNotifyMask(aNotifyMask)
92 // Weak pointer for the nsIWebProgressListener...
93 nsWeakPtr mWeakListener;
95 // Mask indicating which notifications the listener wants to receive.
96 unsigned long mNotifyMask;
100 nsDocLoader::nsDocLoader()
101 : mParent(nullptr),
102 mListenerInfoList(8),
103 mCurrentSelfProgress(0),
104 mMaxSelfProgress(0),
105 mCurrentTotalProgress(0),
106 mMaxTotalProgress(0),
107 mCompletedTotalProgress(0),
108 mIsLoadingDocument(false),
109 mIsRestoringDocument(false),
110 mDontFlushLayout(false),
111 mIsFlushingLayout(false)
113 #if defined(PR_LOGGING)
114 if (nullptr == gDocLoaderLog) {
115 gDocLoaderLog = PR_NewLogModule("DocLoader");
117 #endif /* PR_LOGGING */
119 static const PLDHashTableOps hash_table_ops =
121 PL_DHashAllocTable,
122 PL_DHashFreeTable,
123 PL_DHashVoidPtrKeyStub,
124 PL_DHashMatchEntryStub,
125 PL_DHashMoveEntryStub,
126 RequestInfoHashClearEntry,
127 PL_DHashFinalizeStub,
128 RequestInfoHashInitEntry
131 if (!PL_DHashTableInit(&mRequestInfoHash, &hash_table_ops, nullptr,
132 sizeof(nsRequestInfo), 16)) {
133 mRequestInfoHash.ops = nullptr;
136 ClearInternalProgress();
138 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
139 ("DocLoader:%p: created.\n", this));
142 nsresult
143 nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
145 mParent = aParent;
146 return NS_OK;
149 nsresult
150 nsDocLoader::Init()
152 if (!mRequestInfoHash.ops) {
153 return NS_ERROR_OUT_OF_MEMORY;
156 nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
157 if (NS_FAILED(rv)) return rv;
159 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
160 ("DocLoader:%p: load group %x.\n", this, mLoadGroup.get()));
162 return NS_OK;
165 nsDocLoader::~nsDocLoader()
168 |ClearWeakReferences()| here is intended to prevent people holding weak references
169 from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
170 subsequent |Release()| will try to destroy me. At this point there should be only
171 weak references remaining (otherwise, we wouldn't be getting destroyed).
173 An alternative would be incrementing our refcount (consider it a compressed flag
174 saying "Don't re-destroy."). I haven't yet decided which is better. [scc]
176 // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
177 // this needed?
178 ClearWeakReferences();
180 Destroy();
182 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
183 ("DocLoader:%p: deleted.\n", this));
185 if (mRequestInfoHash.ops) {
186 PL_DHashTableFinish(&mRequestInfoHash);
192 * Implementation of ISupports methods...
194 NS_IMPL_ADDREF(nsDocLoader)
195 NS_IMPL_RELEASE(nsDocLoader)
197 NS_INTERFACE_MAP_BEGIN(nsDocLoader)
198 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
199 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
200 NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
201 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
202 NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
203 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
204 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
205 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
206 NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
207 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
208 if (aIID.Equals(kThisImplCID))
209 foundInterface = static_cast<nsIDocumentLoader *>(this);
210 else
211 NS_INTERFACE_MAP_END
215 * Implementation of nsIInterfaceRequestor methods...
217 NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
219 nsresult rv = NS_ERROR_NO_INTERFACE;
221 NS_ENSURE_ARG_POINTER(aSink);
223 if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
224 *aSink = mLoadGroup;
225 NS_IF_ADDREF((nsISupports*)*aSink);
226 rv = NS_OK;
227 } else {
228 rv = QueryInterface(aIID, aSink);
231 return rv;
234 /* static */
235 already_AddRefed<nsDocLoader>
236 nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
238 nsRefPtr<nsDocLoader> ret = do_QueryObject(aSupports);
239 return ret.forget();
242 /* static */
243 nsresult
244 nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
246 nsresult rv;
247 nsCOMPtr<nsIDocumentLoader> docLoaderService =
248 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
249 NS_ENSURE_SUCCESS(rv, rv);
251 nsRefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
252 NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
254 return rootDocLoader->AddChildLoader(aDocLoader);
257 NS_IMETHODIMP
258 nsDocLoader::Stop(void)
260 nsresult rv = NS_OK;
262 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
263 ("DocLoader:%p: Stop() called\n", this));
265 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader, Stop, ());
267 if (mLoadGroup)
268 rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
270 // Don't report that we're flushing layout so IsBusy returns false after a
271 // Stop call.
272 mIsFlushingLayout = false;
274 // Clear out mChildrenInOnload. We want to make sure to fire our
275 // onload at this point, and there's no issue with mChildrenInOnload
276 // after this, since mDocumentRequest will be null after the
277 // DocLoaderIsEmpty() call.
278 mChildrenInOnload.Clear();
280 // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
281 // etc, as needed. We could be getting into here from a subframe onload, in
282 // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
283 // happened yet, Canceling the loadgroup did nothing (because it was already
284 // empty), and we're about to start a new load (which is what triggered this
285 // Stop() call).
287 // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
288 // we wouldn't need the call here....
290 NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
291 DocLoaderIsEmpty(false);
293 return rv;
297 bool
298 nsDocLoader::IsBusy()
300 nsresult rv;
303 // A document loader is busy if either:
305 // 1. One of its children is in the middle of an onload handler. Note that
306 // the handler may have already removed this child from mChildList!
307 // 2. It is currently loading a document and either has parts of it still
308 // loading, or has a busy child docloader.
309 // 3. It's currently flushing layout in DocLoaderIsEmpty().
312 if (mChildrenInOnload.Count() || mIsFlushingLayout) {
313 return true;
316 /* Is this document loader busy? */
317 if (!mIsLoadingDocument) {
318 return false;
321 bool busy;
322 rv = mLoadGroup->IsPending(&busy);
323 if (NS_FAILED(rv)) {
324 return false;
326 if (busy) {
327 return true;
330 /* check its child document loaders... */
331 uint32_t count = mChildList.Length();
332 for (uint32_t i=0; i < count; i++) {
333 nsIDocumentLoader* loader = ChildAt(i);
335 // This is a safe cast, because we only put nsDocLoader objects into the
336 // array
337 if (loader && static_cast<nsDocLoader*>(loader)->IsBusy())
338 return true;
341 return false;
344 NS_IMETHODIMP
345 nsDocLoader::GetContainer(nsISupports** aResult)
347 NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
349 return NS_OK;
352 NS_IMETHODIMP
353 nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
355 nsresult rv = NS_OK;
357 if (nullptr == aResult) {
358 rv = NS_ERROR_NULL_POINTER;
359 } else {
360 *aResult = mLoadGroup;
361 NS_IF_ADDREF(*aResult);
363 return rv;
366 void
367 nsDocLoader::Destroy()
369 Stop();
371 // Remove the document loader from the parent list of loaders...
372 if (mParent)
374 mParent->RemoveChildLoader(this);
377 // Release all the information about network requests...
378 ClearRequestInfoHash();
380 // Release all the information about registered listeners...
381 int32_t count = mListenerInfoList.Count();
382 for(int32_t i = 0; i < count; i++) {
383 nsListenerInfo *info =
384 static_cast<nsListenerInfo*>(mListenerInfoList.ElementAt(i));
386 delete info;
389 mListenerInfoList.Clear();
390 mListenerInfoList.Compact();
392 mDocumentRequest = 0;
394 if (mLoadGroup)
395 mLoadGroup->SetGroupObserver(nullptr);
397 DestroyChildren();
400 void
401 nsDocLoader::DestroyChildren()
403 uint32_t count = mChildList.Length();
404 // if the doc loader still has children...we need to enumerate the
405 // children and make them null out their back ptr to the parent doc
406 // loader
407 for (uint32_t i=0; i < count; i++)
409 nsIDocumentLoader* loader = ChildAt(i);
411 if (loader) {
412 // This is a safe cast, as we only put nsDocLoader objects into the
413 // array
414 static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nullptr);
417 mChildList.Clear();
420 NS_IMETHODIMP
421 nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
423 // called each time a request is added to the group.
425 #ifdef PR_LOGGING
426 if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
427 nsAutoCString name;
428 request->GetName(name);
430 uint32_t count = 0;
431 if (mLoadGroup)
432 mLoadGroup->GetActiveCount(&count);
434 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
435 ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
436 this, request, name.get(),
437 (mIsLoadingDocument ? "true" : "false"),
438 count));
440 #endif /* PR_LOGGING */
441 bool bJustStartedLoading = false;
443 nsLoadFlags loadFlags = 0;
444 request->GetLoadFlags(&loadFlags);
446 if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
447 bJustStartedLoading = true;
448 mIsLoadingDocument = true;
449 ClearInternalProgress(); // only clear our progress if we are starting a new load....
453 // Create a new nsRequestInfo for the request that is starting to
454 // load...
456 AddRequestInfo(request);
459 // Only fire a doStartDocumentLoad(...) if the document loader
460 // has initiated a load... Otherwise, this notification has
461 // resulted from a request being added to the load group.
463 if (mIsLoadingDocument) {
464 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
466 // Make sure that the document channel is null at this point...
467 // (unless its been redirected)
469 NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
470 !(mDocumentRequest.get()),
471 "Overwriting an existing document channel!");
473 // This request is associated with the entire document...
474 mDocumentRequest = request;
475 mLoadGroup->SetDefaultLoadRequest(request);
477 // Only fire the start document load notification for the first
478 // document URI... Do not fire it again for redirections
480 if (bJustStartedLoading) {
481 // Update the progress status state
482 mProgressStateFlags = nsIWebProgressListener::STATE_START;
484 // Fire the start document load notification
485 doStartDocumentLoad();
486 return NS_OK;
491 NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
492 "mDocumentRequest MUST be set for the duration of a page load!");
494 doStartURLLoad(request);
496 return NS_OK;
499 NS_IMETHODIMP
500 nsDocLoader::OnStopRequest(nsIRequest *aRequest,
501 nsISupports *aCtxt,
502 nsresult aStatus)
504 nsresult rv = NS_OK;
506 #ifdef PR_LOGGING
507 if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
508 nsAutoCString name;
509 aRequest->GetName(name);
511 uint32_t count = 0;
512 if (mLoadGroup)
513 mLoadGroup->GetActiveCount(&count);
515 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
516 ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
517 this, aRequest, name.get(),
518 aStatus, (mIsLoadingDocument ? "true" : "false"),
519 count));
521 #endif
523 bool bFireTransferring = false;
526 // Set the Maximum progress to the same value as the current progress.
527 // Since the URI has finished loading, all the data is there. Also,
528 // this will allow a more accurate estimation of the max progress (in case
529 // the old value was unknown ie. -1)
531 nsRequestInfo *info = GetRequestInfo(aRequest);
532 if (info) {
533 // Null out mLastStatus now so we don't find it when looking for
534 // status from now on. This destroys the nsStatusInfo and hence
535 // removes it from our list.
536 info->mLastStatus = nullptr;
538 int64_t oldMax = info->mMaxProgress;
540 info->mMaxProgress = info->mCurrentProgress;
543 // If a request whose content-length was previously unknown has just
544 // finished loading, then use this new data to try to calculate a
545 // mMaxSelfProgress...
547 if ((oldMax < int64_t(0)) && (mMaxSelfProgress < int64_t(0))) {
548 mMaxSelfProgress = CalculateMaxProgress();
551 // As we know the total progress of this request now, save it to be part
552 // of CalculateMaxProgress() result. We need to remove the info from the
553 // hash, see bug 480713.
554 mCompletedTotalProgress += info->mMaxProgress;
557 // Determine whether a STATE_TRANSFERRING notification should be
558 // 'synthesized'.
560 // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
561 // nsRequestInfo::mCurrentProgress are both 0, then the
562 // STATE_TRANSFERRING notification has not been fired yet...
564 if ((oldMax == 0) && (info->mCurrentProgress == 0)) {
565 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
567 // Only fire a TRANSFERRING notification if the request is also a
568 // channel -- data transfer requires a nsIChannel!
570 if (channel) {
571 if (NS_SUCCEEDED(aStatus)) {
572 bFireTransferring = true;
575 // If the request failed (for any reason other than being
576 // redirected or retargeted), the TRANSFERRING notification can
577 // still be fired if a HTTP connection was established to a server.
579 else if (aStatus != NS_BINDING_REDIRECTED &&
580 aStatus != NS_BINDING_RETARGETED) {
582 // Only if the load has been targeted (see bug 268483)...
584 uint32_t lf;
585 channel->GetLoadFlags(&lf);
586 if (lf & nsIChannel::LOAD_TARGETED) {
587 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
588 if (httpChannel) {
589 uint32_t responseCode;
590 rv = httpChannel->GetResponseStatus(&responseCode);
591 if (NS_SUCCEEDED(rv)) {
593 // A valid server status indicates that a connection was
594 // established to the server... So, fire the notification
595 // even though a failure occurred later...
597 bFireTransferring = true;
606 if (bFireTransferring) {
607 // Send a STATE_TRANSFERRING notification for the request.
608 int32_t flags;
610 flags = nsIWebProgressListener::STATE_TRANSFERRING |
611 nsIWebProgressListener::STATE_IS_REQUEST;
613 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
615 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
616 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
618 // Send STATE_TRANSFERRING for the document too...
619 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
622 FireOnStateChange(this, aRequest, flags, NS_OK);
626 // Fire the OnStateChange(...) notification for stop request
628 doStopURLLoad(aRequest, aStatus);
630 // Clear this request out of the hash to avoid bypass of FireOnStateChange
631 // when address of the request is reused.
632 RemoveRequestInfo(aRequest);
635 // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
636 // load. This will handle removing the request from our hashtable as needed.
638 if (mIsLoadingDocument) {
639 DocLoaderIsEmpty(true);
642 return NS_OK;
646 nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
648 nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
649 if (NS_SUCCEEDED(rv)) {
650 aChild->SetDocLoaderParent(nullptr);
652 return rv;
655 nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
657 nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
658 if (NS_SUCCEEDED(rv)) {
659 aChild->SetDocLoaderParent(this);
661 return rv;
664 NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
666 if (!mDocumentRequest) {
667 *aChannel = nullptr;
668 return NS_OK;
671 return CallQueryInterface(mDocumentRequest, aChannel);
675 void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
677 if (mIsLoadingDocument) {
678 /* In the unimagineably rude circumstance that onload event handlers
679 triggered by this function actually kill the window ... ok, it's
680 not unimagineable; it's happened ... this deathgrip keeps this object
681 alive long enough to survive this function call. */
682 nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
684 // Don't flush layout if we're still busy.
685 if (IsBusy()) {
686 return;
689 NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
691 // The load group for this DocumentLoader is idle. Flush if we need to.
692 if (aFlushLayout && !mDontFlushLayout) {
693 nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(GetAsSupports(this));
694 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
695 if (doc) {
696 // We start loads from style resolution, so we need to flush out style
697 // no matter what. If we have user fonts, we also need to flush layout,
698 // since the reflow is what starts font loads.
699 mozFlushType flushType = Flush_Style;
700 nsIPresShell* shell = doc->GetShell();
701 if (shell) {
702 // Be safe in case this presshell is in teardown now
703 nsPresContext* presContext = shell->GetPresContext();
704 if (presContext && presContext->GetUserFontSet()) {
705 flushType = Flush_Layout;
708 mDontFlushLayout = mIsFlushingLayout = true;
709 doc->FlushPendingNotifications(flushType);
710 mDontFlushLayout = mIsFlushingLayout = false;
714 // And now check whether we're really busy; that might have changed with
715 // the layout flush.
716 if (!IsBusy()) {
717 // Clear out our request info hash, now that our load really is done and
718 // we don't need it anymore to CalculateMaxProgress().
719 ClearInternalProgress();
721 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
722 ("DocLoader:%p: Is now idle...\n", this));
724 nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
726 NS_ASSERTION(mDocumentRequest, "No Document Request!");
727 mDocumentRequest = 0;
728 mIsLoadingDocument = false;
730 // Update the progress status state - the document is done
731 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
734 nsresult loadGroupStatus = NS_OK;
735 mLoadGroup->GetStatus(&loadGroupStatus);
738 // New code to break the circular reference between
739 // the load group and the docloader...
741 mLoadGroup->SetDefaultLoadRequest(nullptr);
743 // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
744 // it even if our onload handler removes us from the docloader tree.
745 nsRefPtr<nsDocLoader> parent = mParent;
747 // Note that if calling ChildEnteringOnload() on the parent returns false
748 // then calling our onload handler is not safe. That can only happen on
749 // OOM, so that's ok.
750 if (!parent || parent->ChildEnteringOnload(this)) {
751 // Do nothing with our state after firing the
752 // OnEndDocumentLoad(...). The document loader may be loading a *new*
753 // document - if LoadDocument() was called from a handler!
755 doStopDocumentLoad(docRequest, loadGroupStatus);
757 if (parent) {
758 parent->ChildDoneWithOnload(this);
765 void nsDocLoader::doStartDocumentLoad(void)
768 #if defined(DEBUG)
769 nsAutoCString buffer;
771 GetURIStringFromRequest(mDocumentRequest, buffer);
772 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
773 ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
774 "\tURI: %s \n",
775 this, buffer.get()));
776 #endif /* DEBUG */
778 // Fire an OnStatus(...) notification STATE_START. This indicates
779 // that the document represented by mDocumentRequest has started to
780 // load...
781 FireOnStateChange(this,
782 mDocumentRequest,
783 nsIWebProgressListener::STATE_START |
784 nsIWebProgressListener::STATE_IS_DOCUMENT |
785 nsIWebProgressListener::STATE_IS_REQUEST |
786 nsIWebProgressListener::STATE_IS_WINDOW |
787 nsIWebProgressListener::STATE_IS_NETWORK,
788 NS_OK);
791 void nsDocLoader::doStartURLLoad(nsIRequest *request)
793 #if defined(DEBUG)
794 nsAutoCString buffer;
796 GetURIStringFromRequest(request, buffer);
797 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
798 ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
799 "\tURI: %s\n",
800 this, buffer.get()));
801 #endif /* DEBUG */
803 FireOnStateChange(this,
804 request,
805 nsIWebProgressListener::STATE_START |
806 nsIWebProgressListener::STATE_IS_REQUEST,
807 NS_OK);
810 void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
812 #if defined(DEBUG)
813 nsAutoCString buffer;
815 GetURIStringFromRequest(request, buffer);
816 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
817 ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
818 "\tURI: %s status=%x\n",
819 this, buffer.get(), aStatus));
820 #endif /* DEBUG */
822 FireOnStateChange(this,
823 request,
824 nsIWebProgressListener::STATE_STOP |
825 nsIWebProgressListener::STATE_IS_REQUEST,
826 aStatus);
828 // Fire a status change message for the most recent unfinished
829 // request to make sure that the displayed status is not outdated.
830 if (!mStatusInfoList.isEmpty()) {
831 nsStatusInfo* statusInfo = mStatusInfoList.getFirst();
832 FireOnStatusChange(this, statusInfo->mRequest,
833 statusInfo->mStatusCode,
834 statusInfo->mStatusMessage.get());
838 void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
839 nsresult aStatus)
841 #if defined(DEBUG)
842 nsAutoCString buffer;
844 GetURIStringFromRequest(request, buffer);
845 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
846 ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
847 "\tURI: %s Status=%x\n",
848 this, buffer.get(), aStatus));
849 #endif /* DEBUG */
851 // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
852 // Grab our parent chain before doing that so we can still dispatch
853 // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
854 // the onload handlers rearrange the docshell tree.
855 WebProgressList list;
856 GatherAncestorWebProgresses(list);
859 // Fire an OnStateChange(...) notification indicating the the
860 // current document has finished loading...
862 int32_t flags = nsIWebProgressListener::STATE_STOP |
863 nsIWebProgressListener::STATE_IS_DOCUMENT;
864 for (uint32_t i = 0; i < list.Length(); ++i) {
865 list[i]->DoFireOnStateChange(this, request, flags, aStatus);
869 // Fire a final OnStateChange(...) notification indicating the the
870 // current document has finished loading...
872 flags = nsIWebProgressListener::STATE_STOP |
873 nsIWebProgressListener::STATE_IS_WINDOW |
874 nsIWebProgressListener::STATE_IS_NETWORK;
875 for (uint32_t i = 0; i < list.Length(); ++i) {
876 list[i]->DoFireOnStateChange(this, request, flags, aStatus);
880 ////////////////////////////////////////////////////////////////////////////////////
881 // The following section contains support for nsIWebProgress and related stuff
882 ////////////////////////////////////////////////////////////////////////////////////
884 NS_IMETHODIMP
885 nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
886 uint32_t aNotifyMask)
888 nsresult rv;
890 nsListenerInfo* info = GetListenerInfo(aListener);
891 if (info) {
892 // The listener is already registered!
893 return NS_ERROR_FAILURE;
896 nsWeakPtr listener = do_GetWeakReference(aListener);
897 if (!listener) {
898 return NS_ERROR_INVALID_ARG;
901 info = new nsListenerInfo(listener, aNotifyMask);
902 if (!info) {
903 return NS_ERROR_OUT_OF_MEMORY;
906 rv = mListenerInfoList.AppendElement(info) ? NS_OK : NS_ERROR_FAILURE;
907 return rv;
910 NS_IMETHODIMP
911 nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
913 nsresult rv;
915 nsListenerInfo* info = GetListenerInfo(aListener);
916 if (info) {
917 rv = mListenerInfoList.RemoveElement(info) ? NS_OK : NS_ERROR_FAILURE;
918 delete info;
919 } else {
920 // The listener is not registered!
921 rv = NS_ERROR_FAILURE;
923 return rv;
926 NS_IMETHODIMP
927 nsDocLoader::GetDOMWindow(nsIDOMWindow **aResult)
929 return CallGetInterface(this, aResult);
932 NS_IMETHODIMP
933 nsDocLoader::GetDOMWindowID(uint64_t *aResult)
935 *aResult = 0;
937 nsCOMPtr<nsIDOMWindow> window;
938 nsresult rv = GetDOMWindow(getter_AddRefs(window));
939 NS_ENSURE_SUCCESS(rv, rv);
941 nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(window);
942 NS_ENSURE_STATE(piwindow);
944 MOZ_ASSERT(piwindow->IsOuterWindow());
945 *aResult = piwindow->WindowID();
946 return NS_OK;
949 NS_IMETHODIMP
950 nsDocLoader::GetIsTopLevel(bool *aResult)
952 *aResult = false;
954 nsCOMPtr<nsIDOMWindow> window;
955 GetDOMWindow(getter_AddRefs(window));
956 if (window) {
957 nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(window);
958 NS_ENSURE_STATE(piwindow);
960 nsCOMPtr<nsIDOMWindow> topWindow;
961 nsresult rv = piwindow->GetTop(getter_AddRefs(topWindow));
962 NS_ENSURE_SUCCESS(rv, rv);
964 *aResult = piwindow == topWindow;
967 return NS_OK;
970 NS_IMETHODIMP
971 nsDocLoader::GetIsLoadingDocument(bool *aIsLoadingDocument)
973 *aIsLoadingDocument = mIsLoadingDocument;
975 return NS_OK;
978 NS_IMETHODIMP
979 nsDocLoader::GetLoadType(uint32_t *aLoadType)
981 *aLoadType = 0;
983 return NS_ERROR_NOT_IMPLEMENTED;
986 int64_t nsDocLoader::GetMaxTotalProgress()
988 int64_t newMaxTotal = 0;
990 uint32_t count = mChildList.Length();
991 nsCOMPtr<nsIWebProgress> webProgress;
992 for (uint32_t i=0; i < count; i++)
994 int64_t individualProgress = 0;
995 nsIDocumentLoader* docloader = ChildAt(i);
996 if (docloader)
998 // Cast is safe since all children are nsDocLoader too
999 individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
1001 if (individualProgress < int64_t(0)) // if one of the elements doesn't know it's size
1002 // then none of them do
1004 newMaxTotal = int64_t(-1);
1005 break;
1007 else
1008 newMaxTotal += individualProgress;
1011 int64_t progress = -1;
1012 if (mMaxSelfProgress >= int64_t(0) && newMaxTotal >= int64_t(0))
1013 progress = newMaxTotal + mMaxSelfProgress;
1015 return progress;
1018 ////////////////////////////////////////////////////////////////////////////////////
1019 // The following section contains support for nsIProgressEventSink which is used to
1020 // pass progress and status between the actual request and the doc loader. The doc loader
1021 // then turns around and makes the right web progress calls based on this information.
1022 ////////////////////////////////////////////////////////////////////////////////////
1024 NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt,
1025 uint64_t aProgress, uint64_t aProgressMax)
1027 nsRequestInfo *info;
1028 int64_t progressDelta = 0;
1031 // Update the RequestInfo entry with the new progress data
1033 info = GetRequestInfo(aRequest);
1034 if (info) {
1035 // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
1036 if (!info->mUploading && (int64_t(0) == info->mCurrentProgress) && (int64_t(0) == info->mMaxProgress)) {
1038 // If we receive an OnProgress event from a toplevel channel that the URI Loader
1039 // has not yet targeted, then we must suppress the event. This is necessary to
1040 // ensure that webprogresslisteners do not get confused when the channel is
1041 // finally targeted. See bug 257308.
1043 nsLoadFlags lf = 0;
1044 aRequest->GetLoadFlags(&lf);
1045 if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
1046 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1047 ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
1048 return NS_OK;
1052 // This is the first progress notification for the entry. If
1053 // (aMaxProgress > 0) then the content-length of the data is known,
1054 // so update mMaxSelfProgress... Otherwise, set it to -1 to indicate
1055 // that the content-length is no longer known.
1057 if (uint64_t(aProgressMax) != UINT64_MAX) {
1058 mMaxSelfProgress += int64_t(aProgressMax);
1059 info->mMaxProgress = int64_t(aProgressMax);
1060 } else {
1061 mMaxSelfProgress = int64_t(-1);
1062 info->mMaxProgress = int64_t(-1);
1065 // Send a STATE_TRANSFERRING notification for the request.
1066 int32_t flags;
1068 flags = nsIWebProgressListener::STATE_TRANSFERRING |
1069 nsIWebProgressListener::STATE_IS_REQUEST;
1071 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
1073 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
1074 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
1076 // Send STATE_TRANSFERRING for the document too...
1077 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1080 FireOnStateChange(this, aRequest, flags, NS_OK);
1083 // Update the current progress count...
1084 progressDelta = int64_t(aProgress) - info->mCurrentProgress;
1085 mCurrentSelfProgress += progressDelta;
1087 info->mCurrentProgress = int64_t(aProgress);
1090 // The request is not part of the load group, so ignore its progress
1091 // information...
1093 else {
1094 #if defined(DEBUG)
1095 nsAutoCString buffer;
1097 GetURIStringFromRequest(aRequest, buffer);
1098 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1099 ("DocLoader:%p OOPS - No Request Info for: %s\n",
1100 this, buffer.get()));
1101 #endif /* DEBUG */
1103 return NS_OK;
1107 // Fire progress notifications out to any registered nsIWebProgressListeners
1109 FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
1110 mCurrentTotalProgress, mMaxTotalProgress);
1112 return NS_OK;
1115 NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt,
1116 nsresult aStatus, const char16_t* aStatusArg)
1119 // Fire progress notifications out to any registered nsIWebProgressListeners
1121 if (aStatus != NS_OK) {
1122 // Remember the current status for this request
1123 nsRequestInfo *info;
1124 info = GetRequestInfo(aRequest);
1125 if (info) {
1126 bool uploading = (aStatus == NS_NET_STATUS_WRITING ||
1127 aStatus == NS_NET_STATUS_SENDING_TO);
1128 // If switching from uploading to downloading (or vice versa), then we
1129 // need to reset our progress counts. This is designed with HTTP form
1130 // submission in mind, where an upload is performed followed by download
1131 // of possibly several documents.
1132 if (info->mUploading != uploading) {
1133 mCurrentSelfProgress = mMaxSelfProgress = 0;
1134 mCurrentTotalProgress = mMaxTotalProgress = 0;
1135 mCompletedTotalProgress = 0;
1136 info->mUploading = uploading;
1137 info->mCurrentProgress = 0;
1138 info->mMaxProgress = 0;
1142 nsCOMPtr<nsIStringBundleService> sbs =
1143 mozilla::services::GetStringBundleService();
1144 if (!sbs)
1145 return NS_ERROR_FAILURE;
1146 nsXPIDLString msg;
1147 nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg,
1148 getter_Copies(msg));
1149 if (NS_FAILED(rv))
1150 return rv;
1152 // Keep around the message. In case a request finishes, we need to make sure
1153 // to send the status message of another request to our user to that we
1154 // don't display, for example, "Transferring" messages for requests that are
1155 // already done.
1156 if (info) {
1157 if (!info->mLastStatus) {
1158 info->mLastStatus = new nsStatusInfo(aRequest);
1159 } else {
1160 // We're going to move it to the front of the list, so remove
1161 // it from wherever it is now.
1162 info->mLastStatus->remove();
1164 info->mLastStatus->mStatusMessage = msg;
1165 info->mLastStatus->mStatusCode = aStatus;
1166 // Put the info at the front of the list
1167 mStatusInfoList.insertFront(info->mLastStatus);
1169 FireOnStatusChange(this, aRequest, aStatus, msg);
1171 return NS_OK;
1174 void nsDocLoader::ClearInternalProgress()
1176 ClearRequestInfoHash();
1178 mCurrentSelfProgress = mMaxSelfProgress = 0;
1179 mCurrentTotalProgress = mMaxTotalProgress = 0;
1180 mCompletedTotalProgress = 0;
1182 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
1186 void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
1187 nsIRequest *request,
1188 int64_t aProgress,
1189 int64_t aProgressMax,
1190 int64_t aProgressDelta,
1191 int64_t aTotalProgress,
1192 int64_t aMaxTotalProgress)
1194 if (mIsLoadingDocument) {
1195 mCurrentTotalProgress += aProgressDelta;
1196 mMaxTotalProgress = GetMaxTotalProgress();
1198 aTotalProgress = mCurrentTotalProgress;
1199 aMaxTotalProgress = mMaxTotalProgress;
1202 #if defined(DEBUG)
1203 nsAutoCString buffer;
1205 GetURIStringFromRequest(request, buffer);
1206 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1207 ("DocLoader:%p: Progress (%s): curSelf: %d maxSelf: %d curTotal: %d maxTotal %d\n",
1208 this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
1209 #endif /* DEBUG */
1212 * First notify any listeners of the new progress info...
1214 * Operate the elements from back to front so that if items get
1215 * get removed from the list it won't affect our iteration
1217 nsCOMPtr<nsIWebProgressListener> listener;
1218 int32_t count = mListenerInfoList.Count();
1220 while (--count >= 0) {
1221 nsListenerInfo *info;
1223 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1224 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_PROGRESS)) {
1225 continue;
1228 listener = do_QueryReferent(info->mWeakListener);
1229 if (!listener) {
1230 // the listener went away. gracefully pull it out of the list.
1231 mListenerInfoList.RemoveElementAt(count);
1232 delete info;
1233 continue;
1236 // XXX truncates 64-bit to 32-bit
1237 listener->OnProgressChange(aLoadInitiator,request,
1238 int32_t(aProgress), int32_t(aProgressMax),
1239 int32_t(aTotalProgress), int32_t(aMaxTotalProgress));
1242 mListenerInfoList.Compact();
1244 // Pass the notification up to the parent...
1245 if (mParent) {
1246 mParent->FireOnProgressChange(aLoadInitiator, request,
1247 aProgress, aProgressMax,
1248 aProgressDelta,
1249 aTotalProgress, aMaxTotalProgress);
1253 void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList)
1255 for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
1256 aList.AppendElement(loader);
1260 void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
1261 nsIRequest *aRequest,
1262 int32_t aStateFlags,
1263 nsresult aStatus)
1265 WebProgressList list;
1266 GatherAncestorWebProgresses(list);
1267 for (uint32_t i = 0; i < list.Length(); ++i) {
1268 list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
1272 void nsDocLoader::DoFireOnStateChange(nsIWebProgress * const aProgress,
1273 nsIRequest * const aRequest,
1274 int32_t &aStateFlags,
1275 const nsresult aStatus)
1278 // Remove the STATE_IS_NETWORK bit if necessary.
1280 // The rule is to remove this bit, if the notification has been passed
1281 // up from a child WebProgress, and the current WebProgress is already
1282 // active...
1284 if (mIsLoadingDocument &&
1285 (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) &&
1286 (this != aProgress)) {
1287 aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
1290 // Add the STATE_RESTORING bit if necessary.
1291 if (mIsRestoringDocument)
1292 aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
1294 #if defined(DEBUG)
1295 nsAutoCString buffer;
1297 GetURIStringFromRequest(aRequest, buffer);
1298 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1299 ("DocLoader:%p: Status (%s): code: %x\n",
1300 this, buffer.get(), aStateFlags));
1301 #endif /* DEBUG */
1303 NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
1306 * First notify any listeners of the new state info...
1308 * Operate the elements from back to front so that if items get
1309 * get removed from the list it won't affect our iteration
1311 nsCOMPtr<nsIWebProgressListener> listener;
1312 int32_t count = mListenerInfoList.Count();
1313 int32_t notifyMask = (aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL;
1315 while (--count >= 0) {
1316 nsListenerInfo *info;
1318 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1319 if (!info || !(info->mNotifyMask & notifyMask)) {
1320 continue;
1323 listener = do_QueryReferent(info->mWeakListener);
1324 if (!listener) {
1325 // the listener went away. gracefully pull it out of the list.
1326 mListenerInfoList.RemoveElementAt(count);
1327 delete info;
1328 continue;
1331 listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
1334 mListenerInfoList.Compact();
1339 void
1340 nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
1341 nsIRequest* aRequest,
1342 nsIURI *aUri,
1343 uint32_t aFlags)
1346 * First notify any listeners of the new state info...
1348 * Operate the elements from back to front so that if items get
1349 * get removed from the list it won't affect our iteration
1351 nsCOMPtr<nsIWebProgressListener> listener;
1352 int32_t count = mListenerInfoList.Count();
1354 while (--count >= 0) {
1355 nsListenerInfo *info;
1357 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1358 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_LOCATION)) {
1359 continue;
1362 listener = do_QueryReferent(info->mWeakListener);
1363 if (!listener) {
1364 // the listener went away. gracefully pull it out of the list.
1365 mListenerInfoList.RemoveElementAt(count);
1366 delete info;
1367 continue;
1370 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, ("DocLoader [%p] calling %p->OnLocationChange", this, listener.get()));
1371 listener->OnLocationChange(aWebProgress, aRequest, aUri, aFlags);
1374 mListenerInfoList.Compact();
1376 // Pass the notification up to the parent...
1377 if (mParent) {
1378 mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
1382 void
1383 nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
1384 nsIRequest* aRequest,
1385 nsresult aStatus,
1386 const char16_t* aMessage)
1389 * First notify any listeners of the new state info...
1391 * Operate the elements from back to front so that if items get
1392 * get removed from the list it won't affect our iteration
1394 nsCOMPtr<nsIWebProgressListener> listener;
1395 int32_t count = mListenerInfoList.Count();
1397 while (--count >= 0) {
1398 nsListenerInfo *info;
1400 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1401 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_STATUS)) {
1402 continue;
1405 listener = do_QueryReferent(info->mWeakListener);
1406 if (!listener) {
1407 // the listener went away. gracefully pull it out of the list.
1408 mListenerInfoList.RemoveElementAt(count);
1409 delete info;
1410 continue;
1413 listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
1415 mListenerInfoList.Compact();
1417 // Pass the notification up to the parent...
1418 if (mParent) {
1419 mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
1423 bool
1424 nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress,
1425 nsIURI *aURI,
1426 int32_t aDelay,
1427 bool aSameURI)
1430 * Returns true if the refresh may proceed,
1431 * false if the refresh should be blocked.
1433 * First notify any listeners of the refresh attempt...
1435 * Iterate the elements from back to front so that if items
1436 * get removed from the list it won't affect our iteration
1438 bool allowRefresh = true;
1439 int32_t count = mListenerInfoList.Count();
1441 while (--count >= 0) {
1442 nsListenerInfo *info;
1444 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1445 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_REFRESH)) {
1446 continue;
1449 nsCOMPtr<nsIWebProgressListener> listener =
1450 do_QueryReferent(info->mWeakListener);
1451 if (!listener) {
1452 // the listener went away. gracefully pull it out of the list.
1453 mListenerInfoList.RemoveElementAt(count);
1454 delete info;
1455 continue;
1458 nsCOMPtr<nsIWebProgressListener2> listener2 =
1459 do_QueryReferent(info->mWeakListener);
1460 if (!listener2)
1461 continue;
1463 bool listenerAllowedRefresh;
1464 nsresult listenerRV = listener2->OnRefreshAttempted(
1465 aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
1466 if (NS_FAILED(listenerRV))
1467 continue;
1469 allowRefresh = allowRefresh && listenerAllowedRefresh;
1472 mListenerInfoList.Compact();
1474 // Pass the notification up to the parent...
1475 if (mParent) {
1476 allowRefresh = allowRefresh &&
1477 mParent->RefreshAttempted(aWebProgress, aURI, aDelay, aSameURI);
1480 return allowRefresh;
1483 nsListenerInfo *
1484 nsDocLoader::GetListenerInfo(nsIWebProgressListener *aListener)
1486 int32_t i, count;
1487 nsListenerInfo *info;
1489 nsCOMPtr<nsISupports> listener1 = do_QueryInterface(aListener);
1490 count = mListenerInfoList.Count();
1491 for (i=0; i<count; i++) {
1492 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(i));
1494 NS_ASSERTION(info, "There should NEVER be a null listener in the list");
1495 if (info) {
1496 nsCOMPtr<nsISupports> listener2 = do_QueryReferent(info->mWeakListener);
1497 if (listener1 == listener2)
1498 return info;
1501 return nullptr;
1504 nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
1506 if (!PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_ADD)) {
1507 return NS_ERROR_OUT_OF_MEMORY;
1510 return NS_OK;
1513 void nsDocLoader::RemoveRequestInfo(nsIRequest *aRequest)
1515 PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_REMOVE);
1518 nsDocLoader::nsRequestInfo* nsDocLoader::GetRequestInfo(nsIRequest* aRequest)
1520 nsRequestInfo* info =
1521 static_cast<nsRequestInfo*>
1522 (PL_DHashTableOperate(&mRequestInfoHash, aRequest,
1523 PL_DHASH_LOOKUP));
1525 if (PL_DHASH_ENTRY_IS_FREE(info)) {
1526 // Nothing found in the hash, return null.
1528 return nullptr;
1531 // Return what we found in the hash...
1533 return info;
1536 // PLDHashTable enumeration callback that just removes every entry
1537 // from the hash.
1538 static PLDHashOperator
1539 RemoveInfoCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
1540 void *arg)
1542 return PL_DHASH_REMOVE;
1545 void nsDocLoader::ClearRequestInfoHash(void)
1547 if (!mRequestInfoHash.ops || !mRequestInfoHash.entryCount) {
1548 // No hash, or the hash is empty, nothing to do here then...
1550 return;
1553 PL_DHashTableEnumerate(&mRequestInfoHash, RemoveInfoCallback, nullptr);
1556 // PLDHashTable enumeration callback that calculates the max progress.
1557 PLDHashOperator
1558 nsDocLoader::CalcMaxProgressCallback(PLDHashTable* table, PLDHashEntryHdr* hdr,
1559 uint32_t number, void* arg)
1561 const nsRequestInfo* info = static_cast<const nsRequestInfo*>(hdr);
1562 int64_t* max = static_cast<int64_t* >(arg);
1564 if (info->mMaxProgress < info->mCurrentProgress) {
1565 *max = int64_t(-1);
1567 return PL_DHASH_STOP;
1570 *max += info->mMaxProgress;
1572 return PL_DHASH_NEXT;
1575 int64_t nsDocLoader::CalculateMaxProgress()
1577 int64_t max = mCompletedTotalProgress;
1578 PL_DHashTableEnumerate(&mRequestInfoHash, CalcMaxProgressCallback, &max);
1579 return max;
1582 NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
1583 nsIChannel *aNewChannel,
1584 uint32_t aFlags,
1585 nsIAsyncVerifyRedirectCallback *cb)
1587 if (aOldChannel)
1589 nsLoadFlags loadFlags = 0;
1590 int32_t stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
1591 nsIWebProgressListener::STATE_IS_REQUEST;
1593 aOldChannel->GetLoadFlags(&loadFlags);
1594 // If the document channel is being redirected, then indicate that the
1595 // document is being redirected in the notification...
1596 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
1598 stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1600 #if defined(DEBUG)
1601 nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
1602 NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
1603 #endif /* DEBUG */
1606 OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
1607 FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
1610 cb->OnRedirectVerifyCallback(NS_OK);
1611 return NS_OK;
1615 * Implementation of nsISecurityEventSink method...
1618 NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
1619 uint32_t aState)
1622 // Fire progress notifications out to any registered nsIWebProgressListeners.
1625 nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
1626 nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
1629 * First notify any listeners of the new state info...
1631 * Operate the elements from back to front so that if items get
1632 * get removed from the list it won't affect our iteration
1634 nsCOMPtr<nsIWebProgressListener> listener;
1635 int32_t count = mListenerInfoList.Count();
1637 while (--count >= 0) {
1638 nsListenerInfo *info;
1640 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1641 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_SECURITY)) {
1642 continue;
1645 listener = do_QueryReferent(info->mWeakListener);
1646 if (!listener) {
1647 // the listener went away. gracefully pull it out of the list.
1648 mListenerInfoList.RemoveElementAt(count);
1649 delete info;
1650 continue;
1653 listener->OnSecurityChange(webProgress, request, aState);
1656 mListenerInfoList.Compact();
1658 // Pass the notification up to the parent...
1659 if (mParent) {
1660 mParent->OnSecurityChange(aContext, aState);
1662 return NS_OK;
1666 * Implementation of nsISupportsPriority methods...
1668 * The priority of the DocLoader _is_ the priority of its LoadGroup.
1670 * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
1671 * go away.
1674 NS_IMETHODIMP nsDocLoader::GetPriority(int32_t *aPriority)
1676 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1677 if (p)
1678 return p->GetPriority(aPriority);
1680 *aPriority = 0;
1681 return NS_OK;
1684 NS_IMETHODIMP nsDocLoader::SetPriority(int32_t aPriority)
1686 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1687 ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
1689 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1690 if (p)
1691 p->SetPriority(aPriority);
1693 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
1694 SetPriority, (aPriority));
1696 return NS_OK;
1699 NS_IMETHODIMP nsDocLoader::AdjustPriority(int32_t aDelta)
1701 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1702 ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
1704 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1705 if (p)
1706 p->AdjustPriority(aDelta);
1708 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
1709 AdjustPriority, (aDelta));
1711 return NS_OK;
1717 #if 0
1718 void nsDocLoader::DumpChannelInfo()
1720 nsChannelInfo *info;
1721 int32_t i, count;
1722 int32_t current=0, max=0;
1725 printf("==== DocLoader=%x\n", this);
1727 count = mChannelInfoList.Count();
1728 for(i=0; i<count; i++) {
1729 info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
1731 #if defined(DEBUG)
1732 nsAutoCString buffer;
1733 nsresult rv = NS_OK;
1734 if (info->mURI) {
1735 rv = info->mURI->GetSpec(buffer);
1738 printf(" [%d] current=%d max=%d [%s]\n", i,
1739 info->mCurrentProgress,
1740 info->mMaxProgress, buffer.get());
1741 #endif /* DEBUG */
1743 current += info->mCurrentProgress;
1744 if (max >= 0) {
1745 if (info->mMaxProgress < info->mCurrentProgress) {
1746 max = -1;
1747 } else {
1748 max += info->mMaxProgress;
1753 printf("\nCurrent=%d Total=%d\n====\n", current, max);
1755 #endif /* 0 */