uriloader/base/nsDocLoader.cpp

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

mercurial