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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : *
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.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nspr.h"
39 : #include "prlog.h"
40 :
41 : #include "nsDocLoader.h"
42 : #include "nsCURILoader.h"
43 : #include "nsNetUtil.h"
44 : #include "nsIHttpChannel.h"
45 : #include "nsIWebProgressListener2.h"
46 :
47 : #include "nsIServiceManager.h"
48 : #include "nsXPIDLString.h"
49 :
50 : #include "nsIURL.h"
51 : #include "nsCOMPtr.h"
52 : #include "nscore.h"
53 : #include "nsWeakPtr.h"
54 : #include "nsAutoPtr.h"
55 :
56 : #include "nsIDOMWindow.h"
57 :
58 : #include "nsIStringBundle.h"
59 : #include "nsIScriptSecurityManager.h"
60 :
61 : #include "nsITransport.h"
62 : #include "nsISocketTransport.h"
63 :
64 : #include "nsIDOMDocument.h"
65 : #include "nsIDocument.h"
66 : #include "nsPresContext.h"
67 : #include "nsIAsyncVerifyRedirectCallback.h"
68 :
69 : static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
70 :
71 : #if defined(PR_LOGGING)
72 : //
73 : // Log module for nsIDocumentLoader logging...
74 : //
75 : // To enable logging (see prlog.h for full details):
76 : //
77 : // set NSPR_LOG_MODULES=DocLoader:5
78 : // set NSPR_LOG_FILE=nspr.log
79 : //
80 : // this enables PR_LOG_DEBUG level information and places all output in
81 : // the file nspr.log
82 : //
83 : PRLogModuleInfo* gDocLoaderLog = nsnull;
84 : #endif /* PR_LOGGING */
85 :
86 :
87 : #if defined(DEBUG)
88 0 : void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
89 : {
90 0 : if (request)
91 0 : request->GetName(name);
92 : else
93 0 : name.AssignLiteral("???");
94 0 : }
95 : #endif /* DEBUG */
96 :
97 : struct nsStatusInfo : public PRCList
98 : {
99 : nsString mStatusMessage;
100 : nsresult mStatusCode;
101 : // Weak mRequest is ok; we'll be told if it decides to go away.
102 : nsIRequest * const mRequest;
103 :
104 0 : nsStatusInfo(nsIRequest *aRequest) :
105 0 : mRequest(aRequest)
106 : {
107 0 : MOZ_COUNT_CTOR(nsStatusInfo);
108 0 : PR_INIT_CLIST(this);
109 0 : }
110 0 : ~nsStatusInfo()
111 0 : {
112 0 : MOZ_COUNT_DTOR(nsStatusInfo);
113 0 : PR_REMOVE_LINK(this);
114 0 : }
115 : };
116 :
117 : struct nsRequestInfo : public PLDHashEntryHdr
118 : {
119 0 : nsRequestInfo(const void *key)
120 : : mKey(key), mCurrentProgress(0), mMaxProgress(0), mUploading(false)
121 0 : , mLastStatus(nsnull)
122 : {
123 0 : MOZ_COUNT_CTOR(nsRequestInfo);
124 0 : }
125 :
126 0 : ~nsRequestInfo()
127 0 : {
128 0 : MOZ_COUNT_DTOR(nsRequestInfo);
129 0 : }
130 :
131 : nsIRequest* Request() {
132 : return static_cast<nsIRequest*>(const_cast<void*>(mKey));
133 : }
134 :
135 : const void* mKey; // Must be first for the pldhash stubs to work
136 : PRInt64 mCurrentProgress;
137 : PRInt64 mMaxProgress;
138 : bool mUploading;
139 :
140 : nsAutoPtr<nsStatusInfo> mLastStatus;
141 : };
142 :
143 :
144 : static bool
145 0 : RequestInfoHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
146 : const void *key)
147 : {
148 : // Initialize the entry with placement new
149 0 : new (entry) nsRequestInfo(key);
150 0 : return true;
151 : }
152 :
153 : static void
154 0 : RequestInfoHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
155 : {
156 0 : nsRequestInfo* info = static_cast<nsRequestInfo *>(entry);
157 0 : info->~nsRequestInfo();
158 0 : }
159 :
160 1445 : struct nsListenerInfo {
161 1445 : nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask)
162 : : mWeakListener(aListener),
163 1445 : mNotifyMask(aNotifyMask)
164 : {
165 1445 : }
166 :
167 : // Weak pointer for the nsIWebProgressListener...
168 : nsWeakPtr mWeakListener;
169 :
170 : // Mask indicating which notifications the listener wants to receive.
171 : unsigned long mNotifyMask;
172 : };
173 :
174 :
175 1404 : nsDocLoader::nsDocLoader()
176 : : mParent(nsnull),
177 : mListenerInfoList(8),
178 : mCurrentSelfProgress(0),
179 : mMaxSelfProgress(0),
180 : mCurrentTotalProgress(0),
181 : mMaxTotalProgress(0),
182 : mCompletedTotalProgress(0),
183 : mIsLoadingDocument(false),
184 : mIsRestoringDocument(false),
185 : mDontFlushLayout(false),
186 1404 : mIsFlushingLayout(false)
187 : {
188 : #if defined(PR_LOGGING)
189 1404 : if (nsnull == gDocLoaderLog) {
190 1404 : gDocLoaderLog = PR_NewLogModule("DocLoader");
191 : }
192 : #endif /* PR_LOGGING */
193 :
194 : static PLDHashTableOps hash_table_ops =
195 : {
196 : PL_DHashAllocTable,
197 : PL_DHashFreeTable,
198 : PL_DHashVoidPtrKeyStub,
199 : PL_DHashMatchEntryStub,
200 : PL_DHashMoveEntryStub,
201 : RequestInfoHashClearEntry,
202 : PL_DHashFinalizeStub,
203 : RequestInfoHashInitEntry
204 : };
205 :
206 1404 : if (!PL_DHashTableInit(&mRequestInfoHash, &hash_table_ops, nsnull,
207 1404 : sizeof(nsRequestInfo), 16)) {
208 0 : mRequestInfoHash.ops = nsnull;
209 : }
210 :
211 1404 : ClearInternalProgress();
212 :
213 1404 : PR_INIT_CLIST(&mStatusInfoList);
214 :
215 1404 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
216 : ("DocLoader:%p: created.\n", this));
217 1404 : }
218 :
219 : nsresult
220 0 : nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
221 : {
222 0 : mParent = aParent;
223 0 : return NS_OK;
224 : }
225 :
226 : nsresult
227 1404 : nsDocLoader::Init()
228 : {
229 1404 : if (!mRequestInfoHash.ops) {
230 0 : return NS_ERROR_OUT_OF_MEMORY;
231 : }
232 :
233 1404 : nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
234 1404 : if (NS_FAILED(rv)) return rv;
235 :
236 1404 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
237 : ("DocLoader:%p: load group %x.\n", this, mLoadGroup.get()));
238 :
239 1404 : return NS_OK;
240 : }
241 :
242 4212 : nsDocLoader::~nsDocLoader()
243 : {
244 : /*
245 : |ClearWeakReferences()| here is intended to prevent people holding weak references
246 : from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
247 : subsequent |Release()| will try to destroy me. At this point there should be only
248 : weak references remaining (otherwise, we wouldn't be getting destroyed).
249 :
250 : An alternative would be incrementing our refcount (consider it a compressed flag
251 : saying "Don't re-destroy."). I haven't yet decided which is better. [scc]
252 : */
253 : // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
254 : // this needed?
255 1404 : ClearWeakReferences();
256 :
257 1404 : Destroy();
258 :
259 1404 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
260 : ("DocLoader:%p: deleted.\n", this));
261 :
262 1404 : if (mRequestInfoHash.ops) {
263 1404 : PL_DHashTableFinish(&mRequestInfoHash);
264 : }
265 5616 : }
266 :
267 :
268 : /*
269 : * Implementation of ISupports methods...
270 : */
271 7182 : NS_IMPL_THREADSAFE_ADDREF(nsDocLoader)
272 7182 : NS_IMPL_THREADSAFE_RELEASE(nsDocLoader)
273 :
274 3162 : NS_INTERFACE_MAP_BEGIN(nsDocLoader)
275 3162 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
276 3123 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
277 3123 : NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
278 3123 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
279 1719 : NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
280 234 : NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
281 234 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
282 234 : NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
283 234 : NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
284 234 : NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
285 234 : if (aIID.Equals(kThisImplCID))
286 0 : foundInterface = static_cast<nsIDocumentLoader *>(this);
287 : else
288 234 : NS_INTERFACE_MAP_END
289 :
290 :
291 : /*
292 : * Implementation of nsIInterfaceRequestor methods...
293 : */
294 0 : NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
295 : {
296 0 : nsresult rv = NS_ERROR_NO_INTERFACE;
297 :
298 0 : NS_ENSURE_ARG_POINTER(aSink);
299 :
300 0 : if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
301 0 : *aSink = mLoadGroup;
302 0 : NS_IF_ADDREF((nsISupports*)*aSink);
303 0 : rv = NS_OK;
304 : } else {
305 0 : rv = QueryInterface(aIID, aSink);
306 : }
307 :
308 0 : return rv;
309 : }
310 :
311 : /* static */
312 : already_AddRefed<nsDocLoader>
313 0 : nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
314 : {
315 0 : if (!aSupports) {
316 0 : return nsnull;
317 : }
318 :
319 : nsDocLoader* ptr;
320 0 : CallQueryInterface(aSupports, &ptr);
321 0 : return ptr;
322 : }
323 :
324 : /* static */
325 : nsresult
326 0 : nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
327 : {
328 : nsresult rv;
329 : nsCOMPtr<nsIDocumentLoader> docLoaderService =
330 0 : do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
331 0 : NS_ENSURE_SUCCESS(rv, rv);
332 :
333 0 : nsRefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
334 0 : NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
335 :
336 0 : return rootDocLoader->AddChildLoader(aDocLoader);
337 : }
338 :
339 : NS_IMETHODIMP
340 1404 : nsDocLoader::Stop(void)
341 : {
342 1404 : nsresult rv = NS_OK;
343 : PRInt32 count, i;
344 :
345 1404 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
346 : ("DocLoader:%p: Stop() called\n", this));
347 :
348 1404 : count = mChildList.Count();
349 :
350 2808 : nsCOMPtr<nsIDocumentLoader> loader;
351 1404 : for (i=0; i < count; i++) {
352 0 : loader = ChildAt(i);
353 :
354 0 : if (loader) {
355 0 : (void) loader->Stop();
356 : }
357 : }
358 :
359 1404 : if (mLoadGroup)
360 1404 : rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
361 :
362 : // Don't report that we're flushing layout so IsBusy returns false after a
363 : // Stop call.
364 1404 : mIsFlushingLayout = false;
365 :
366 : // Clear out mChildrenInOnload. We want to make sure to fire our
367 : // onload at this point, and there's no issue with mChildrenInOnload
368 : // after this, since mDocumentRequest will be null after the
369 : // DocLoaderIsEmpty() call.
370 1404 : mChildrenInOnload.Clear();
371 :
372 : // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
373 : // etc, as needed. We could be getting into here from a subframe onload, in
374 : // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
375 : // happened yet, Canceling the loadgroup did nothing (because it was already
376 : // empty), and we're about to start a new load (which is what triggered this
377 : // Stop() call).
378 :
379 : // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
380 : // we wouldn't need the call here....
381 :
382 1404 : NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
383 1404 : DocLoaderIsEmpty(false);
384 :
385 1404 : return rv;
386 : }
387 :
388 :
389 : bool
390 1404 : nsDocLoader::IsBusy()
391 : {
392 : nsresult rv;
393 :
394 : //
395 : // A document loader is busy if either:
396 : //
397 : // 1. One of its children is in the middle of an onload handler. Note that
398 : // the handler may have already removed this child from mChildList!
399 : // 2. It is currently loading a document and either has parts of it still
400 : // loading, or has a busy child docloader.
401 : // 3. It's currently flushing layout in DocLoaderIsEmpty().
402 : //
403 :
404 1404 : if (mChildrenInOnload.Count() || mIsFlushingLayout) {
405 0 : return true;
406 : }
407 :
408 : /* Is this document loader busy? */
409 1404 : if (!mIsLoadingDocument) {
410 1404 : return false;
411 : }
412 :
413 : bool busy;
414 0 : rv = mLoadGroup->IsPending(&busy);
415 0 : if (NS_FAILED(rv)) {
416 0 : return false;
417 : }
418 0 : if (busy) {
419 0 : return true;
420 : }
421 :
422 : /* check its child document loaders... */
423 : PRInt32 count, i;
424 :
425 0 : count = mChildList.Count();
426 :
427 0 : for (i=0; i < count; i++) {
428 0 : nsIDocumentLoader* loader = ChildAt(i);
429 :
430 : // This is a safe cast, because we only put nsDocLoader objects into the
431 : // array
432 0 : if (loader && static_cast<nsDocLoader*>(loader)->IsBusy())
433 0 : return true;
434 : }
435 :
436 0 : return false;
437 : }
438 :
439 : NS_IMETHODIMP
440 0 : nsDocLoader::GetContainer(nsISupports** aResult)
441 : {
442 0 : NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
443 :
444 0 : return NS_OK;
445 : }
446 :
447 : NS_IMETHODIMP
448 0 : nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
449 : {
450 0 : nsresult rv = NS_OK;
451 :
452 0 : if (nsnull == aResult) {
453 0 : rv = NS_ERROR_NULL_POINTER;
454 : } else {
455 0 : *aResult = mLoadGroup;
456 0 : NS_IF_ADDREF(*aResult);
457 : }
458 0 : return rv;
459 : }
460 :
461 : void
462 1404 : nsDocLoader::Destroy()
463 : {
464 1404 : Stop();
465 :
466 : // Remove the document loader from the parent list of loaders...
467 1404 : if (mParent)
468 : {
469 0 : mParent->RemoveChildLoader(this);
470 : }
471 :
472 : // Release all the information about network requests...
473 1404 : ClearRequestInfoHash();
474 :
475 : // Release all the information about registered listeners...
476 1404 : PRInt32 count = mListenerInfoList.Count();
477 2848 : for(PRInt32 i = 0; i < count; i++) {
478 : nsListenerInfo *info =
479 1444 : static_cast<nsListenerInfo*>(mListenerInfoList.ElementAt(i));
480 :
481 1444 : delete info;
482 : }
483 :
484 1404 : mListenerInfoList.Clear();
485 1404 : mListenerInfoList.Compact();
486 :
487 1404 : mDocumentRequest = 0;
488 :
489 1404 : if (mLoadGroup)
490 1404 : mLoadGroup->SetGroupObserver(nsnull);
491 :
492 1404 : DestroyChildren();
493 1404 : }
494 :
495 : void
496 1404 : nsDocLoader::DestroyChildren()
497 : {
498 : PRInt32 i, count;
499 :
500 1404 : count = mChildList.Count();
501 : // if the doc loader still has children...we need to enumerate the
502 : // children and make them null out their back ptr to the parent doc
503 : // loader
504 1404 : for (i=0; i < count; i++)
505 : {
506 0 : nsIDocumentLoader* loader = ChildAt(i);
507 :
508 0 : if (loader) {
509 : // This is a safe cast, as we only put nsDocLoader objects into the
510 : // array
511 0 : static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nsnull);
512 : }
513 : }
514 1404 : mChildList.Clear();
515 1404 : }
516 :
517 : NS_IMETHODIMP
518 0 : nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
519 : {
520 : // called each time a request is added to the group.
521 :
522 : #ifdef PR_LOGGING
523 0 : if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
524 0 : nsCAutoString name;
525 0 : request->GetName(name);
526 :
527 0 : PRUint32 count = 0;
528 0 : if (mLoadGroup)
529 0 : mLoadGroup->GetActiveCount(&count);
530 :
531 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
532 : ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
533 : this, request, name.get(),
534 : (mIsLoadingDocument ? "true" : "false"),
535 : count));
536 : }
537 : #endif /* PR_LOGGING */
538 0 : bool bJustStartedLoading = false;
539 :
540 0 : nsLoadFlags loadFlags = 0;
541 0 : request->GetLoadFlags(&loadFlags);
542 :
543 0 : if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
544 0 : bJustStartedLoading = true;
545 0 : mIsLoadingDocument = true;
546 0 : ClearInternalProgress(); // only clear our progress if we are starting a new load....
547 : }
548 :
549 : //
550 : // Create a new nsRequestInfo for the request that is starting to
551 : // load...
552 : //
553 0 : AddRequestInfo(request);
554 :
555 : //
556 : // Only fire a doStartDocumentLoad(...) if the document loader
557 : // has initiated a load... Otherwise, this notification has
558 : // resulted from a request being added to the load group.
559 : //
560 0 : if (mIsLoadingDocument) {
561 0 : if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
562 : //
563 : // Make sure that the document channel is null at this point...
564 : // (unless its been redirected)
565 : //
566 0 : NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
567 : !(mDocumentRequest.get()),
568 : "Overwriting an existing document channel!");
569 :
570 : // This request is associated with the entire document...
571 0 : mDocumentRequest = request;
572 0 : mLoadGroup->SetDefaultLoadRequest(request);
573 :
574 : // Only fire the start document load notification for the first
575 : // document URI... Do not fire it again for redirections
576 : //
577 0 : if (bJustStartedLoading) {
578 : // Update the progress status state
579 0 : mProgressStateFlags = nsIWebProgressListener::STATE_START;
580 :
581 : // Fire the start document load notification
582 0 : doStartDocumentLoad();
583 0 : return NS_OK;
584 : }
585 : }
586 : }
587 :
588 0 : NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
589 : "mDocumentRequest MUST be set for the duration of a page load!");
590 :
591 0 : doStartURLLoad(request);
592 :
593 0 : return NS_OK;
594 : }
595 :
596 : NS_IMETHODIMP
597 0 : nsDocLoader::OnStopRequest(nsIRequest *aRequest,
598 : nsISupports *aCtxt,
599 : nsresult aStatus)
600 : {
601 0 : nsresult rv = NS_OK;
602 :
603 : #ifdef PR_LOGGING
604 0 : if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
605 0 : nsCAutoString name;
606 0 : aRequest->GetName(name);
607 :
608 0 : PRUint32 count = 0;
609 0 : if (mLoadGroup)
610 0 : mLoadGroup->GetActiveCount(&count);
611 :
612 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
613 : ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
614 : this, aRequest, name.get(),
615 : aStatus, (mIsLoadingDocument ? "true" : "false"),
616 : count));
617 : }
618 : #endif
619 :
620 0 : bool bFireTransferring = false;
621 :
622 : //
623 : // Set the Maximum progress to the same value as the current progress.
624 : // Since the URI has finished loading, all the data is there. Also,
625 : // this will allow a more accurate estimation of the max progress (in case
626 : // the old value was unknown ie. -1)
627 : //
628 0 : nsRequestInfo *info = GetRequestInfo(aRequest);
629 0 : if (info) {
630 : // Null out mLastStatus now so we don't find it when looking for
631 : // status from now on. This destroys the nsStatusInfo and hence
632 : // removes it from our list.
633 0 : info->mLastStatus = nsnull;
634 :
635 0 : PRInt64 oldMax = info->mMaxProgress;
636 :
637 0 : info->mMaxProgress = info->mCurrentProgress;
638 :
639 : //
640 : // If a request whose content-length was previously unknown has just
641 : // finished loading, then use this new data to try to calculate a
642 : // mMaxSelfProgress...
643 : //
644 0 : if ((oldMax < PRInt64(0)) && (mMaxSelfProgress < PRInt64(0))) {
645 0 : mMaxSelfProgress = CalculateMaxProgress();
646 : }
647 :
648 : // As we know the total progress of this request now, save it to be part
649 : // of CalculateMaxProgress() result. We need to remove the info from the
650 : // hash, see bug 480713.
651 0 : mCompletedTotalProgress += info->mMaxProgress;
652 :
653 : //
654 : // Determine whether a STATE_TRANSFERRING notification should be
655 : // 'synthesized'.
656 : //
657 : // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
658 : // nsRequestInfo::mCurrentProgress are both 0, then the
659 : // STATE_TRANSFERRING notification has not been fired yet...
660 : //
661 0 : if ((oldMax == LL_ZERO) && (info->mCurrentProgress == LL_ZERO)) {
662 0 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
663 :
664 : // Only fire a TRANSFERRING notification if the request is also a
665 : // channel -- data transfer requires a nsIChannel!
666 : //
667 0 : if (channel) {
668 0 : if (NS_SUCCEEDED(aStatus)) {
669 0 : bFireTransferring = true;
670 : }
671 : //
672 : // If the request failed (for any reason other than being
673 : // redirected or retargeted), the TRANSFERRING notification can
674 : // still be fired if a HTTP connection was established to a server.
675 : //
676 0 : else if (aStatus != NS_BINDING_REDIRECTED &&
677 : aStatus != NS_BINDING_RETARGETED) {
678 : //
679 : // Only if the load has been targeted (see bug 268483)...
680 : //
681 : PRUint32 lf;
682 0 : channel->GetLoadFlags(&lf);
683 0 : if (lf & nsIChannel::LOAD_TARGETED) {
684 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
685 0 : if (httpChannel) {
686 : PRUint32 responseCode;
687 0 : rv = httpChannel->GetResponseStatus(&responseCode);
688 0 : if (NS_SUCCEEDED(rv)) {
689 : //
690 : // A valid server status indicates that a connection was
691 : // established to the server... So, fire the notification
692 : // even though a failure occurred later...
693 : //
694 0 : bFireTransferring = true;
695 : }
696 : }
697 : }
698 : }
699 : }
700 : }
701 : }
702 :
703 0 : if (bFireTransferring) {
704 : // Send a STATE_TRANSFERRING notification for the request.
705 : PRInt32 flags;
706 :
707 : flags = nsIWebProgressListener::STATE_TRANSFERRING |
708 0 : nsIWebProgressListener::STATE_IS_REQUEST;
709 : //
710 : // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
711 : //
712 0 : if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
713 0 : mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
714 :
715 : // Send STATE_TRANSFERRING for the document too...
716 0 : flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
717 : }
718 :
719 0 : FireOnStateChange(this, aRequest, flags, NS_OK);
720 : }
721 :
722 : //
723 : // Fire the OnStateChange(...) notification for stop request
724 : //
725 0 : doStopURLLoad(aRequest, aStatus);
726 :
727 : // Clear this request out of the hash to avoid bypass of FireOnStateChange
728 : // when address of the request is reused.
729 0 : RemoveRequestInfo(aRequest);
730 :
731 : //
732 : // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
733 : // load. This will handle removing the request from our hashtable as needed.
734 : //
735 0 : if (mIsLoadingDocument) {
736 0 : DocLoaderIsEmpty(true);
737 : }
738 :
739 0 : return NS_OK;
740 : }
741 :
742 :
743 0 : nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
744 : {
745 0 : nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
746 0 : if (NS_SUCCEEDED(rv)) {
747 0 : aChild->SetDocLoaderParent(nsnull);
748 : }
749 0 : return rv;
750 : }
751 :
752 0 : nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
753 : {
754 0 : nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
755 0 : if (NS_SUCCEEDED(rv)) {
756 0 : aChild->SetDocLoaderParent(this);
757 : }
758 0 : return rv;
759 : }
760 :
761 0 : NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
762 : {
763 0 : if (!mDocumentRequest) {
764 0 : *aChannel = nsnull;
765 0 : return NS_OK;
766 : }
767 :
768 0 : return CallQueryInterface(mDocumentRequest, aChannel);
769 : }
770 :
771 :
772 1404 : void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
773 : {
774 1404 : if (mIsLoadingDocument) {
775 : /* In the unimagineably rude circumstance that onload event handlers
776 : triggered by this function actually kill the window ... ok, it's
777 : not unimagineable; it's happened ... this deathgrip keeps this object
778 : alive long enough to survive this function call. */
779 0 : nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
780 :
781 : // Don't flush layout if we're still busy.
782 0 : if (IsBusy()) {
783 : return;
784 : }
785 :
786 0 : NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
787 :
788 : // The load group for this DocumentLoader is idle. Flush if we need to.
789 0 : if (aFlushLayout && !mDontFlushLayout) {
790 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(GetAsSupports(this));
791 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
792 0 : if (doc) {
793 : // We start loads from style resolution, so we need to flush out style
794 : // no matter what. If we have user fonts, we also need to flush layout,
795 : // since the reflow is what starts font loads.
796 0 : mozFlushType flushType = Flush_Style;
797 0 : nsIPresShell* shell = doc->GetShell();
798 0 : if (shell) {
799 : // Be safe in case this presshell is in teardown now
800 0 : nsPresContext* presContext = shell->GetPresContext();
801 0 : if (presContext && presContext->GetUserFontSet()) {
802 0 : flushType = Flush_Layout;
803 : }
804 : }
805 0 : mDontFlushLayout = mIsFlushingLayout = true;
806 0 : doc->FlushPendingNotifications(flushType);
807 0 : mDontFlushLayout = mIsFlushingLayout = false;
808 : }
809 : }
810 :
811 : // And now check whether we're really busy; that might have changed with
812 : // the layout flush.
813 0 : if (!IsBusy()) {
814 : // Clear out our request info hash, now that our load really is done and
815 : // we don't need it anymore to CalculateMaxProgress().
816 0 : ClearInternalProgress();
817 :
818 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
819 : ("DocLoader:%p: Is now idle...\n", this));
820 :
821 0 : nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
822 :
823 0 : NS_ASSERTION(mDocumentRequest, "No Document Request!");
824 0 : mDocumentRequest = 0;
825 0 : mIsLoadingDocument = false;
826 :
827 : // Update the progress status state - the document is done
828 0 : mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
829 :
830 :
831 0 : nsresult loadGroupStatus = NS_OK;
832 0 : mLoadGroup->GetStatus(&loadGroupStatus);
833 :
834 : //
835 : // New code to break the circular reference between
836 : // the load group and the docloader...
837 : //
838 0 : mLoadGroup->SetDefaultLoadRequest(nsnull);
839 :
840 : // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
841 : // it even if our onload handler removes us from the docloader tree.
842 0 : nsRefPtr<nsDocLoader> parent = mParent;
843 :
844 : // Note that if calling ChildEnteringOnload() on the parent returns false
845 : // then calling our onload handler is not safe. That can only happen on
846 : // OOM, so that's ok.
847 0 : if (!parent || parent->ChildEnteringOnload(this)) {
848 : // Do nothing with our state after firing the
849 : // OnEndDocumentLoad(...). The document loader may be loading a *new*
850 : // document - if LoadDocument() was called from a handler!
851 : //
852 0 : doStopDocumentLoad(docRequest, loadGroupStatus);
853 :
854 0 : if (parent) {
855 0 : parent->ChildDoneWithOnload(this);
856 : }
857 : }
858 : }
859 : }
860 : }
861 :
862 0 : void nsDocLoader::doStartDocumentLoad(void)
863 : {
864 :
865 : #if defined(DEBUG)
866 0 : nsCAutoString buffer;
867 :
868 0 : GetURIStringFromRequest(mDocumentRequest, buffer);
869 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
870 : ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
871 : "\tURI: %s \n",
872 : this, buffer.get()));
873 : #endif /* DEBUG */
874 :
875 : // Fire an OnStatus(...) notification STATE_START. This indicates
876 : // that the document represented by mDocumentRequest has started to
877 : // load...
878 : FireOnStateChange(this,
879 : mDocumentRequest,
880 : nsIWebProgressListener::STATE_START |
881 : nsIWebProgressListener::STATE_IS_DOCUMENT |
882 : nsIWebProgressListener::STATE_IS_REQUEST |
883 : nsIWebProgressListener::STATE_IS_WINDOW |
884 : nsIWebProgressListener::STATE_IS_NETWORK,
885 0 : NS_OK);
886 0 : }
887 :
888 0 : void nsDocLoader::doStartURLLoad(nsIRequest *request)
889 : {
890 : #if defined(DEBUG)
891 0 : nsCAutoString buffer;
892 :
893 0 : GetURIStringFromRequest(request, buffer);
894 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
895 : ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
896 : "\tURI: %s\n",
897 : this, buffer.get()));
898 : #endif /* DEBUG */
899 :
900 : FireOnStateChange(this,
901 : request,
902 : nsIWebProgressListener::STATE_START |
903 : nsIWebProgressListener::STATE_IS_REQUEST,
904 0 : NS_OK);
905 0 : }
906 :
907 0 : void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
908 : {
909 : #if defined(DEBUG)
910 0 : nsCAutoString buffer;
911 :
912 0 : GetURIStringFromRequest(request, buffer);
913 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
914 : ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
915 : "\tURI: %s status=%x\n",
916 : this, buffer.get(), aStatus));
917 : #endif /* DEBUG */
918 :
919 : FireOnStateChange(this,
920 : request,
921 : nsIWebProgressListener::STATE_STOP |
922 : nsIWebProgressListener::STATE_IS_REQUEST,
923 0 : aStatus);
924 :
925 : // Fire a status change message for the most recent unfinished
926 : // request to make sure that the displayed status is not outdated.
927 0 : if (!PR_CLIST_IS_EMPTY(&mStatusInfoList)) {
928 : nsStatusInfo* statusInfo =
929 0 : static_cast<nsStatusInfo*>(PR_LIST_HEAD(&mStatusInfoList));
930 : FireOnStatusChange(this, statusInfo->mRequest,
931 : statusInfo->mStatusCode,
932 0 : statusInfo->mStatusMessage.get());
933 : }
934 0 : }
935 :
936 0 : void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
937 : nsresult aStatus)
938 : {
939 : #if defined(DEBUG)
940 0 : nsCAutoString buffer;
941 :
942 0 : GetURIStringFromRequest(request, buffer);
943 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
944 : ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
945 : "\tURI: %s Status=%x\n",
946 : this, buffer.get(), aStatus));
947 : #endif /* DEBUG */
948 :
949 : // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
950 : // Grab our parent chain before doing that so we can still dispatch
951 : // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
952 : // the onload handlers rearrange the docshell tree.
953 0 : WebProgressList list;
954 0 : GatherAncestorWebProgresses(list);
955 :
956 : //
957 : // Fire an OnStateChange(...) notification indicating the the
958 : // current document has finished loading...
959 : //
960 : PRInt32 flags = nsIWebProgressListener::STATE_STOP |
961 0 : nsIWebProgressListener::STATE_IS_DOCUMENT;
962 0 : for (PRUint32 i = 0; i < list.Length(); ++i) {
963 0 : list[i]->DoFireOnStateChange(this, request, flags, aStatus);
964 : }
965 :
966 : //
967 : // Fire a final OnStateChange(...) notification indicating the the
968 : // current document has finished loading...
969 : //
970 : flags = nsIWebProgressListener::STATE_STOP |
971 : nsIWebProgressListener::STATE_IS_WINDOW |
972 0 : nsIWebProgressListener::STATE_IS_NETWORK;
973 0 : for (PRUint32 i = 0; i < list.Length(); ++i) {
974 0 : list[i]->DoFireOnStateChange(this, request, flags, aStatus);
975 : }
976 0 : }
977 :
978 : ////////////////////////////////////////////////////////////////////////////////////
979 : // The following section contains support for nsIWebProgress and related stuff
980 : ////////////////////////////////////////////////////////////////////////////////////
981 :
982 : NS_IMETHODIMP
983 1445 : nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
984 : PRUint32 aNotifyMask)
985 : {
986 : nsresult rv;
987 :
988 1445 : nsListenerInfo* info = GetListenerInfo(aListener);
989 1445 : if (info) {
990 : // The listener is already registered!
991 0 : return NS_ERROR_FAILURE;
992 : }
993 :
994 2890 : nsWeakPtr listener = do_GetWeakReference(aListener);
995 1445 : if (!listener) {
996 0 : return NS_ERROR_INVALID_ARG;
997 : }
998 :
999 2890 : info = new nsListenerInfo(listener, aNotifyMask);
1000 1445 : if (!info) {
1001 0 : return NS_ERROR_OUT_OF_MEMORY;
1002 : }
1003 :
1004 1445 : rv = mListenerInfoList.AppendElement(info) ? NS_OK : NS_ERROR_FAILURE;
1005 1445 : return rv;
1006 : }
1007 :
1008 : NS_IMETHODIMP
1009 1 : nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
1010 : {
1011 : nsresult rv;
1012 :
1013 1 : nsListenerInfo* info = GetListenerInfo(aListener);
1014 1 : if (info) {
1015 1 : rv = mListenerInfoList.RemoveElement(info) ? NS_OK : NS_ERROR_FAILURE;
1016 1 : delete info;
1017 : } else {
1018 : // The listener is not registered!
1019 0 : rv = NS_ERROR_FAILURE;
1020 : }
1021 1 : return rv;
1022 : }
1023 :
1024 : NS_IMETHODIMP
1025 0 : nsDocLoader::GetDOMWindow(nsIDOMWindow **aResult)
1026 : {
1027 0 : return CallGetInterface(this, aResult);
1028 : }
1029 :
1030 : NS_IMETHODIMP
1031 0 : nsDocLoader::GetIsLoadingDocument(bool *aIsLoadingDocument)
1032 : {
1033 0 : *aIsLoadingDocument = mIsLoadingDocument;
1034 :
1035 0 : return NS_OK;
1036 : }
1037 :
1038 0 : PRInt64 nsDocLoader::GetMaxTotalProgress()
1039 : {
1040 0 : PRInt64 newMaxTotal = 0;
1041 :
1042 0 : PRInt32 count = mChildList.Count();
1043 0 : nsCOMPtr<nsIWebProgress> webProgress;
1044 0 : for (PRInt32 i=0; i < count; i++)
1045 : {
1046 0 : PRInt64 individualProgress = 0;
1047 0 : nsIDocumentLoader* docloader = ChildAt(i);
1048 0 : if (docloader)
1049 : {
1050 : // Cast is safe since all children are nsDocLoader too
1051 0 : individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
1052 : }
1053 0 : if (individualProgress < PRInt64(0)) // if one of the elements doesn't know it's size
1054 : // then none of them do
1055 : {
1056 0 : newMaxTotal = PRInt64(-1);
1057 0 : break;
1058 : }
1059 : else
1060 0 : newMaxTotal += individualProgress;
1061 : }
1062 :
1063 0 : PRInt64 progress = -1;
1064 0 : if (mMaxSelfProgress >= PRInt64(0) && newMaxTotal >= PRInt64(0))
1065 0 : progress = newMaxTotal + mMaxSelfProgress;
1066 :
1067 0 : return progress;
1068 : }
1069 :
1070 : ////////////////////////////////////////////////////////////////////////////////////
1071 : // The following section contains support for nsIProgressEventSink which is used to
1072 : // pass progress and status between the actual request and the doc loader. The doc loader
1073 : // then turns around and makes the right web progress calls based on this information.
1074 : ////////////////////////////////////////////////////////////////////////////////////
1075 :
1076 0 : NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt,
1077 : PRUint64 aProgress, PRUint64 aProgressMax)
1078 : {
1079 : nsRequestInfo *info;
1080 0 : PRInt64 progressDelta = 0;
1081 :
1082 : //
1083 : // Update the RequestInfo entry with the new progress data
1084 : //
1085 0 : info = GetRequestInfo(aRequest);
1086 0 : if (info) {
1087 : // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
1088 0 : if (!info->mUploading && (PRInt64(0) == info->mCurrentProgress) && (PRInt64(0) == info->mMaxProgress)) {
1089 : //
1090 : // If we receive an OnProgress event from a toplevel channel that the URI Loader
1091 : // has not yet targeted, then we must suppress the event. This is necessary to
1092 : // ensure that webprogresslisteners do not get confused when the channel is
1093 : // finally targeted. See bug 257308.
1094 : //
1095 0 : nsLoadFlags lf = 0;
1096 0 : aRequest->GetLoadFlags(&lf);
1097 0 : if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
1098 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1099 : ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
1100 0 : return NS_OK;
1101 : }
1102 :
1103 : //
1104 : // This is the first progress notification for the entry. If
1105 : // (aMaxProgress > 0) then the content-length of the data is known,
1106 : // so update mMaxSelfProgress... Otherwise, set it to -1 to indicate
1107 : // that the content-length is no longer known.
1108 : //
1109 0 : if (PRUint64(aProgressMax) != LL_MAXUINT) {
1110 0 : mMaxSelfProgress += PRInt64(aProgressMax);
1111 0 : info->mMaxProgress = PRInt64(aProgressMax);
1112 : } else {
1113 0 : mMaxSelfProgress = PRInt64(-1);
1114 0 : info->mMaxProgress = PRInt64(-1);
1115 : }
1116 :
1117 : // Send a STATE_TRANSFERRING notification for the request.
1118 : PRInt32 flags;
1119 :
1120 : flags = nsIWebProgressListener::STATE_TRANSFERRING |
1121 0 : nsIWebProgressListener::STATE_IS_REQUEST;
1122 : //
1123 : // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
1124 : //
1125 0 : if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
1126 0 : mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
1127 :
1128 : // Send STATE_TRANSFERRING for the document too...
1129 0 : flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1130 : }
1131 :
1132 0 : FireOnStateChange(this, aRequest, flags, NS_OK);
1133 : }
1134 :
1135 : // Update the current progress count...
1136 0 : progressDelta = PRInt64(aProgress) - info->mCurrentProgress;
1137 0 : mCurrentSelfProgress += progressDelta;
1138 :
1139 0 : info->mCurrentProgress = PRInt64(aProgress);
1140 : }
1141 : //
1142 : // The request is not part of the load group, so ignore its progress
1143 : // information...
1144 : //
1145 : else {
1146 : #if defined(DEBUG)
1147 0 : nsCAutoString buffer;
1148 :
1149 0 : GetURIStringFromRequest(aRequest, buffer);
1150 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1151 : ("DocLoader:%p OOPS - No Request Info for: %s\n",
1152 : this, buffer.get()));
1153 : #endif /* DEBUG */
1154 :
1155 0 : return NS_OK;
1156 : }
1157 :
1158 : //
1159 : // Fire progress notifications out to any registered nsIWebProgressListeners
1160 : //
1161 : FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
1162 0 : mCurrentTotalProgress, mMaxTotalProgress);
1163 :
1164 0 : return NS_OK;
1165 : }
1166 :
1167 0 : NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt,
1168 : nsresult aStatus, const PRUnichar* aStatusArg)
1169 : {
1170 : //
1171 : // Fire progress notifications out to any registered nsIWebProgressListeners
1172 : //
1173 0 : if (aStatus) {
1174 : // Remember the current status for this request
1175 : nsRequestInfo *info;
1176 0 : info = GetRequestInfo(aRequest);
1177 0 : if (info) {
1178 : bool uploading = (aStatus == nsITransport::STATUS_WRITING ||
1179 0 : aStatus == nsISocketTransport::STATUS_SENDING_TO);
1180 : // If switching from uploading to downloading (or vice versa), then we
1181 : // need to reset our progress counts. This is designed with HTTP form
1182 : // submission in mind, where an upload is performed followed by download
1183 : // of possibly several documents.
1184 0 : if (info->mUploading != uploading) {
1185 0 : mCurrentSelfProgress = mMaxSelfProgress = LL_ZERO;
1186 0 : mCurrentTotalProgress = mMaxTotalProgress = LL_ZERO;
1187 0 : mCompletedTotalProgress = LL_ZERO;
1188 0 : info->mUploading = uploading;
1189 0 : info->mCurrentProgress = LL_ZERO;
1190 0 : info->mMaxProgress = LL_ZERO;
1191 : }
1192 : }
1193 :
1194 : nsCOMPtr<nsIStringBundleService> sbs =
1195 0 : mozilla::services::GetStringBundleService();
1196 0 : if (!sbs)
1197 0 : return NS_ERROR_FAILURE;
1198 0 : nsXPIDLString msg;
1199 0 : nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg,
1200 0 : getter_Copies(msg));
1201 0 : if (NS_FAILED(rv))
1202 0 : return rv;
1203 :
1204 : // Keep around the message. In case a request finishes, we need to make sure
1205 : // to send the status message of another request to our user to that we
1206 : // don't display, for example, "Transferring" messages for requests that are
1207 : // already done.
1208 0 : if (info) {
1209 0 : if (!info->mLastStatus) {
1210 0 : info->mLastStatus = new nsStatusInfo(aRequest);
1211 : } else {
1212 : // We're going to move it to the front of the list, so remove
1213 : // it from wherever it is now.
1214 0 : PR_REMOVE_LINK(info->mLastStatus);
1215 : }
1216 0 : info->mLastStatus->mStatusMessage = msg;
1217 0 : info->mLastStatus->mStatusCode = aStatus;
1218 : // Put the info at the front of the list
1219 0 : PR_INSERT_LINK(info->mLastStatus, &mStatusInfoList);
1220 : }
1221 0 : FireOnStatusChange(this, aRequest, aStatus, msg);
1222 : }
1223 0 : return NS_OK;
1224 : }
1225 :
1226 1404 : void nsDocLoader::ClearInternalProgress()
1227 : {
1228 1404 : ClearRequestInfoHash();
1229 :
1230 1404 : mCurrentSelfProgress = mMaxSelfProgress = LL_ZERO;
1231 1404 : mCurrentTotalProgress = mMaxTotalProgress = LL_ZERO;
1232 1404 : mCompletedTotalProgress = LL_ZERO;
1233 :
1234 1404 : mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
1235 1404 : }
1236 :
1237 :
1238 0 : void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
1239 : nsIRequest *request,
1240 : PRInt64 aProgress,
1241 : PRInt64 aProgressMax,
1242 : PRInt64 aProgressDelta,
1243 : PRInt64 aTotalProgress,
1244 : PRInt64 aMaxTotalProgress)
1245 : {
1246 0 : if (mIsLoadingDocument) {
1247 0 : mCurrentTotalProgress += aProgressDelta;
1248 0 : mMaxTotalProgress = GetMaxTotalProgress();
1249 :
1250 0 : aTotalProgress = mCurrentTotalProgress;
1251 0 : aMaxTotalProgress = mMaxTotalProgress;
1252 : }
1253 :
1254 : #if defined(DEBUG)
1255 0 : nsCAutoString buffer;
1256 :
1257 0 : GetURIStringFromRequest(request, buffer);
1258 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1259 : ("DocLoader:%p: Progress (%s): curSelf: %d maxSelf: %d curTotal: %d maxTotal %d\n",
1260 : this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
1261 : #endif /* DEBUG */
1262 :
1263 : /*
1264 : * First notify any listeners of the new progress info...
1265 : *
1266 : * Operate the elements from back to front so that if items get
1267 : * get removed from the list it won't affect our iteration
1268 : */
1269 0 : nsCOMPtr<nsIWebProgressListener> listener;
1270 0 : PRInt32 count = mListenerInfoList.Count();
1271 :
1272 0 : while (--count >= 0) {
1273 : nsListenerInfo *info;
1274 :
1275 0 : info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1276 0 : if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_PROGRESS)) {
1277 0 : continue;
1278 : }
1279 :
1280 0 : listener = do_QueryReferent(info->mWeakListener);
1281 0 : if (!listener) {
1282 : // the listener went away. gracefully pull it out of the list.
1283 0 : mListenerInfoList.RemoveElementAt(count);
1284 0 : delete info;
1285 0 : continue;
1286 : }
1287 :
1288 : // XXX truncates 64-bit to 32-bit
1289 0 : listener->OnProgressChange(aLoadInitiator,request,
1290 : PRInt32(aProgress), PRInt32(aProgressMax),
1291 0 : PRInt32(aTotalProgress), PRInt32(aMaxTotalProgress));
1292 : }
1293 :
1294 0 : mListenerInfoList.Compact();
1295 :
1296 : // Pass the notification up to the parent...
1297 0 : if (mParent) {
1298 : mParent->FireOnProgressChange(aLoadInitiator, request,
1299 : aProgress, aProgressMax,
1300 : aProgressDelta,
1301 0 : aTotalProgress, aMaxTotalProgress);
1302 : }
1303 0 : }
1304 :
1305 0 : void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList)
1306 : {
1307 0 : for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
1308 0 : aList.AppendElement(loader);
1309 : }
1310 0 : }
1311 :
1312 0 : void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
1313 : nsIRequest *aRequest,
1314 : PRInt32 aStateFlags,
1315 : nsresult aStatus)
1316 : {
1317 0 : WebProgressList list;
1318 0 : GatherAncestorWebProgresses(list);
1319 0 : for (PRUint32 i = 0; i < list.Length(); ++i) {
1320 0 : list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
1321 : }
1322 0 : }
1323 :
1324 0 : void nsDocLoader::DoFireOnStateChange(nsIWebProgress * const aProgress,
1325 : nsIRequest * const aRequest,
1326 : PRInt32 &aStateFlags,
1327 : const nsresult aStatus)
1328 : {
1329 : //
1330 : // Remove the STATE_IS_NETWORK bit if necessary.
1331 : //
1332 : // The rule is to remove this bit, if the notification has been passed
1333 : // up from a child WebProgress, and the current WebProgress is already
1334 : // active...
1335 : //
1336 0 : if (mIsLoadingDocument &&
1337 : (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) &&
1338 : (this != aProgress)) {
1339 0 : aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
1340 : }
1341 :
1342 : // Add the STATE_RESTORING bit if necessary.
1343 0 : if (mIsRestoringDocument)
1344 0 : aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
1345 :
1346 : #if defined(DEBUG)
1347 0 : nsCAutoString buffer;
1348 :
1349 0 : GetURIStringFromRequest(aRequest, buffer);
1350 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1351 : ("DocLoader:%p: Status (%s): code: %x\n",
1352 : this, buffer.get(), aStateFlags));
1353 : #endif /* DEBUG */
1354 :
1355 0 : NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
1356 :
1357 : /*
1358 : * First notify any listeners of the new state info...
1359 : *
1360 : * Operate the elements from back to front so that if items get
1361 : * get removed from the list it won't affect our iteration
1362 : */
1363 0 : nsCOMPtr<nsIWebProgressListener> listener;
1364 0 : PRInt32 count = mListenerInfoList.Count();
1365 0 : PRInt32 notifyMask = (aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL;
1366 :
1367 0 : while (--count >= 0) {
1368 : nsListenerInfo *info;
1369 :
1370 0 : info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1371 0 : if (!info || !(info->mNotifyMask & notifyMask)) {
1372 0 : continue;
1373 : }
1374 :
1375 0 : listener = do_QueryReferent(info->mWeakListener);
1376 0 : if (!listener) {
1377 : // the listener went away. gracefully pull it out of the list.
1378 0 : mListenerInfoList.RemoveElementAt(count);
1379 0 : delete info;
1380 0 : continue;
1381 : }
1382 :
1383 0 : listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
1384 : }
1385 :
1386 0 : mListenerInfoList.Compact();
1387 0 : }
1388 :
1389 :
1390 :
1391 : void
1392 0 : nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
1393 : nsIRequest* aRequest,
1394 : nsIURI *aUri,
1395 : PRUint32 aFlags)
1396 : {
1397 : /*
1398 : * First notify any listeners of the new state info...
1399 : *
1400 : * Operate the elements from back to front so that if items get
1401 : * get removed from the list it won't affect our iteration
1402 : */
1403 0 : nsCOMPtr<nsIWebProgressListener> listener;
1404 0 : PRInt32 count = mListenerInfoList.Count();
1405 :
1406 0 : while (--count >= 0) {
1407 : nsListenerInfo *info;
1408 :
1409 0 : info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1410 0 : if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_LOCATION)) {
1411 0 : continue;
1412 : }
1413 :
1414 0 : listener = do_QueryReferent(info->mWeakListener);
1415 0 : if (!listener) {
1416 : // the listener went away. gracefully pull it out of the list.
1417 0 : mListenerInfoList.RemoveElementAt(count);
1418 0 : delete info;
1419 0 : continue;
1420 : }
1421 :
1422 0 : listener->OnLocationChange(aWebProgress, aRequest, aUri, aFlags);
1423 : }
1424 :
1425 0 : mListenerInfoList.Compact();
1426 :
1427 : // Pass the notification up to the parent...
1428 0 : if (mParent) {
1429 0 : mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
1430 : }
1431 0 : }
1432 :
1433 : void
1434 0 : nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
1435 : nsIRequest* aRequest,
1436 : nsresult aStatus,
1437 : const PRUnichar* aMessage)
1438 : {
1439 : /*
1440 : * First notify any listeners of the new state info...
1441 : *
1442 : * Operate the elements from back to front so that if items get
1443 : * get removed from the list it won't affect our iteration
1444 : */
1445 0 : nsCOMPtr<nsIWebProgressListener> listener;
1446 0 : PRInt32 count = mListenerInfoList.Count();
1447 :
1448 0 : while (--count >= 0) {
1449 : nsListenerInfo *info;
1450 :
1451 0 : info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1452 0 : if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_STATUS)) {
1453 0 : continue;
1454 : }
1455 :
1456 0 : listener = do_QueryReferent(info->mWeakListener);
1457 0 : if (!listener) {
1458 : // the listener went away. gracefully pull it out of the list.
1459 0 : mListenerInfoList.RemoveElementAt(count);
1460 0 : delete info;
1461 0 : continue;
1462 : }
1463 :
1464 0 : listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
1465 : }
1466 0 : mListenerInfoList.Compact();
1467 :
1468 : // Pass the notification up to the parent...
1469 0 : if (mParent) {
1470 0 : mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
1471 : }
1472 0 : }
1473 :
1474 : bool
1475 0 : nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress,
1476 : nsIURI *aURI,
1477 : PRInt32 aDelay,
1478 : bool aSameURI)
1479 : {
1480 : /*
1481 : * Returns true if the refresh may proceed,
1482 : * false if the refresh should be blocked.
1483 : *
1484 : * First notify any listeners of the refresh attempt...
1485 : *
1486 : * Iterate the elements from back to front so that if items
1487 : * get removed from the list it won't affect our iteration
1488 : */
1489 0 : bool allowRefresh = true;
1490 0 : PRInt32 count = mListenerInfoList.Count();
1491 :
1492 0 : while (--count >= 0) {
1493 : nsListenerInfo *info;
1494 :
1495 0 : info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1496 0 : if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_REFRESH)) {
1497 0 : continue;
1498 : }
1499 :
1500 : nsCOMPtr<nsIWebProgressListener> listener =
1501 0 : do_QueryReferent(info->mWeakListener);
1502 0 : if (!listener) {
1503 : // the listener went away. gracefully pull it out of the list.
1504 0 : mListenerInfoList.RemoveElementAt(count);
1505 0 : delete info;
1506 0 : continue;
1507 : }
1508 :
1509 : nsCOMPtr<nsIWebProgressListener2> listener2 =
1510 0 : do_QueryReferent(info->mWeakListener);
1511 0 : if (!listener2)
1512 0 : continue;
1513 :
1514 : bool listenerAllowedRefresh;
1515 0 : nsresult listenerRV = listener2->OnRefreshAttempted(
1516 0 : aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
1517 0 : if (NS_FAILED(listenerRV))
1518 0 : continue;
1519 :
1520 0 : allowRefresh = allowRefresh && listenerAllowedRefresh;
1521 : }
1522 :
1523 0 : mListenerInfoList.Compact();
1524 :
1525 : // Pass the notification up to the parent...
1526 0 : if (mParent) {
1527 : allowRefresh = allowRefresh &&
1528 0 : mParent->RefreshAttempted(aWebProgress, aURI, aDelay, aSameURI);
1529 : }
1530 :
1531 0 : return allowRefresh;
1532 : }
1533 :
1534 : nsListenerInfo *
1535 1446 : nsDocLoader::GetListenerInfo(nsIWebProgressListener *aListener)
1536 : {
1537 : PRInt32 i, count;
1538 : nsListenerInfo *info;
1539 :
1540 2892 : nsCOMPtr<nsISupports> listener1 = do_QueryInterface(aListener);
1541 1446 : count = mListenerInfoList.Count();
1542 1488 : for (i=0; i<count; i++) {
1543 43 : info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(i));
1544 :
1545 43 : NS_ASSERTION(info, "There should NEVER be a null listener in the list");
1546 43 : if (info) {
1547 86 : nsCOMPtr<nsISupports> listener2 = do_QueryReferent(info->mWeakListener);
1548 43 : if (listener1 == listener2)
1549 1 : return info;
1550 : }
1551 : }
1552 1445 : return nsnull;
1553 : }
1554 :
1555 0 : nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
1556 : {
1557 0 : if (!PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_ADD)) {
1558 0 : return NS_ERROR_OUT_OF_MEMORY;
1559 : }
1560 :
1561 0 : return NS_OK;
1562 : }
1563 :
1564 0 : void nsDocLoader::RemoveRequestInfo(nsIRequest *aRequest)
1565 : {
1566 0 : PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_REMOVE);
1567 0 : }
1568 :
1569 0 : nsRequestInfo * nsDocLoader::GetRequestInfo(nsIRequest *aRequest)
1570 : {
1571 : nsRequestInfo *info =
1572 : static_cast<nsRequestInfo *>
1573 : (PL_DHashTableOperate(&mRequestInfoHash, aRequest,
1574 0 : PL_DHASH_LOOKUP));
1575 :
1576 0 : if (PL_DHASH_ENTRY_IS_FREE(info)) {
1577 : // Nothing found in the hash, return null.
1578 :
1579 0 : return nsnull;
1580 : }
1581 :
1582 : // Return what we found in the hash...
1583 :
1584 0 : return info;
1585 : }
1586 :
1587 : // PLDHashTable enumeration callback that just removes every entry
1588 : // from the hash.
1589 : static PLDHashOperator
1590 0 : RemoveInfoCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number,
1591 : void *arg)
1592 : {
1593 0 : return PL_DHASH_REMOVE;
1594 : }
1595 :
1596 2808 : void nsDocLoader::ClearRequestInfoHash(void)
1597 : {
1598 2808 : if (!mRequestInfoHash.ops || !mRequestInfoHash.entryCount) {
1599 : // No hash, or the hash is empty, nothing to do here then...
1600 :
1601 2808 : return;
1602 : }
1603 :
1604 0 : PL_DHashTableEnumerate(&mRequestInfoHash, RemoveInfoCallback, nsnull);
1605 : }
1606 :
1607 : // PLDHashTable enumeration callback that calculates the max progress.
1608 : static PLDHashOperator
1609 0 : CalcMaxProgressCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
1610 : PRUint32 number, void *arg)
1611 : {
1612 0 : const nsRequestInfo *info = static_cast<const nsRequestInfo *>(hdr);
1613 0 : PRInt64 *max = static_cast<PRInt64 *>(arg);
1614 :
1615 0 : if (info->mMaxProgress < info->mCurrentProgress) {
1616 0 : *max = PRInt64(-1);
1617 :
1618 0 : return PL_DHASH_STOP;
1619 : }
1620 :
1621 0 : *max += info->mMaxProgress;
1622 :
1623 0 : return PL_DHASH_NEXT;
1624 : }
1625 :
1626 0 : PRInt64 nsDocLoader::CalculateMaxProgress()
1627 : {
1628 0 : PRInt64 max = mCompletedTotalProgress;
1629 0 : PL_DHashTableEnumerate(&mRequestInfoHash, CalcMaxProgressCallback, &max);
1630 0 : return max;
1631 : }
1632 :
1633 0 : NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
1634 : nsIChannel *aNewChannel,
1635 : PRUint32 aFlags,
1636 : nsIAsyncVerifyRedirectCallback *cb)
1637 : {
1638 0 : if (aOldChannel)
1639 : {
1640 0 : nsLoadFlags loadFlags = 0;
1641 : PRInt32 stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
1642 0 : nsIWebProgressListener::STATE_IS_REQUEST;
1643 :
1644 0 : aOldChannel->GetLoadFlags(&loadFlags);
1645 : // If the document channel is being redirected, then indicate that the
1646 : // document is being redirected in the notification...
1647 0 : if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
1648 : {
1649 0 : stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1650 :
1651 : #if defined(DEBUG)
1652 0 : nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
1653 0 : NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
1654 : #endif /* DEBUG */
1655 : }
1656 :
1657 0 : OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
1658 0 : FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
1659 : }
1660 :
1661 0 : cb->OnRedirectVerifyCallback(NS_OK);
1662 0 : return NS_OK;
1663 : }
1664 :
1665 : /*
1666 : * Implementation of nsISecurityEventSink method...
1667 : */
1668 :
1669 0 : NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
1670 : PRUint32 aState)
1671 : {
1672 : //
1673 : // Fire progress notifications out to any registered nsIWebProgressListeners.
1674 : //
1675 :
1676 0 : nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
1677 0 : nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
1678 :
1679 : /*
1680 : * First notify any listeners of the new state info...
1681 : *
1682 : * Operate the elements from back to front so that if items get
1683 : * get removed from the list it won't affect our iteration
1684 : */
1685 0 : nsCOMPtr<nsIWebProgressListener> listener;
1686 0 : PRInt32 count = mListenerInfoList.Count();
1687 :
1688 0 : while (--count >= 0) {
1689 : nsListenerInfo *info;
1690 :
1691 0 : info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
1692 0 : if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_SECURITY)) {
1693 0 : continue;
1694 : }
1695 :
1696 0 : listener = do_QueryReferent(info->mWeakListener);
1697 0 : if (!listener) {
1698 : // the listener went away. gracefully pull it out of the list.
1699 0 : mListenerInfoList.RemoveElementAt(count);
1700 0 : delete info;
1701 0 : continue;
1702 : }
1703 :
1704 0 : listener->OnSecurityChange(webProgress, request, aState);
1705 : }
1706 :
1707 0 : mListenerInfoList.Compact();
1708 :
1709 : // Pass the notification up to the parent...
1710 0 : if (mParent) {
1711 0 : mParent->OnSecurityChange(aContext, aState);
1712 : }
1713 0 : return NS_OK;
1714 : }
1715 :
1716 : /*
1717 : * Implementation of nsISupportsPriority methods...
1718 : *
1719 : * The priority of the DocLoader _is_ the priority of its LoadGroup.
1720 : *
1721 : * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
1722 : * go away.
1723 : */
1724 :
1725 0 : NS_IMETHODIMP nsDocLoader::GetPriority(PRInt32 *aPriority)
1726 : {
1727 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1728 0 : if (p)
1729 0 : return p->GetPriority(aPriority);
1730 :
1731 0 : *aPriority = 0;
1732 0 : return NS_OK;
1733 : }
1734 :
1735 0 : NS_IMETHODIMP nsDocLoader::SetPriority(PRInt32 aPriority)
1736 : {
1737 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1738 : ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
1739 :
1740 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1741 0 : if (p)
1742 0 : p->SetPriority(aPriority);
1743 :
1744 0 : PRInt32 count = mChildList.Count();
1745 :
1746 : nsDocLoader *loader;
1747 0 : for (PRInt32 i=0; i < count; i++) {
1748 0 : loader = static_cast<nsDocLoader*>(ChildAt(i));
1749 0 : if (loader) {
1750 0 : loader->SetPriority(aPriority);
1751 : }
1752 : }
1753 :
1754 0 : return NS_OK;
1755 : }
1756 :
1757 0 : NS_IMETHODIMP nsDocLoader::AdjustPriority(PRInt32 aDelta)
1758 : {
1759 0 : PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
1760 : ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
1761 :
1762 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1763 0 : if (p)
1764 0 : p->AdjustPriority(aDelta);
1765 :
1766 0 : PRInt32 count = mChildList.Count();
1767 :
1768 : nsDocLoader *loader;
1769 0 : for (PRInt32 i=0; i < count; i++) {
1770 0 : loader = static_cast<nsDocLoader*>(ChildAt(i));
1771 0 : if (loader) {
1772 0 : loader->AdjustPriority(aDelta);
1773 : }
1774 : }
1775 :
1776 0 : return NS_OK;
1777 : }
1778 :
1779 :
1780 :
1781 :
1782 : #if 0
1783 : void nsDocLoader::DumpChannelInfo()
1784 : {
1785 : nsChannelInfo *info;
1786 : PRInt32 i, count;
1787 : PRInt32 current=0, max=0;
1788 :
1789 :
1790 : printf("==== DocLoader=%x\n", this);
1791 :
1792 : count = mChannelInfoList.Count();
1793 : for(i=0; i<count; i++) {
1794 : info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
1795 :
1796 : #if defined(DEBUG)
1797 : nsCAutoString buffer;
1798 : nsresult rv = NS_OK;
1799 : if (info->mURI) {
1800 : rv = info->mURI->GetSpec(buffer);
1801 : }
1802 :
1803 : printf(" [%d] current=%d max=%d [%s]\n", i,
1804 : info->mCurrentProgress,
1805 : info->mMaxProgress, buffer.get());
1806 : #endif /* DEBUG */
1807 :
1808 : current += info->mCurrentProgress;
1809 : if (max >= 0) {
1810 : if (info->mMaxProgress < info->mCurrentProgress) {
1811 : max = -1;
1812 : } else {
1813 : max += info->mMaxProgress;
1814 : }
1815 : }
1816 : }
1817 :
1818 : printf("\nCurrent=%d Total=%d\n====\n", current, max);
1819 : }
1820 : #endif /* 0 */
|