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/. */
7 #include "mozilla/dom/BrowserChild.h"
8 #include "mozilla/dom/Document.h"
9 #include "mozilla/BasicEvents.h"
10 #include "mozilla/Components.h"
11 #include "mozilla/EventDispatcher.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/IntegerPrintfMacros.h"
14 #include "mozilla/PresShell.h"
16 #include "nsDocLoader.h"
17 #include "nsDocShell.h"
18 #include "nsLoadGroup.h"
19 #include "nsNetUtil.h"
20 #include "nsIHttpChannel.h"
21 #include "nsIWebNavigation.h"
22 #include "nsIWebProgressListener2.h"
28 #include "nsIWeakReferenceUtils.h"
29 #include "nsQueryObject.h"
31 #include "nsPIDOMWindow.h"
33 #include "nsIStringBundle.h"
35 #include "nsIDocShell.h"
36 #include "mozilla/dom/Document.h"
37 #include "mozilla/dom/DocGroup.h"
38 #include "nsPresContext.h"
39 #include "nsIAsyncVerifyRedirectCallback.h"
40 #include "nsIBrowserDOMWindow.h"
41 #include "mozilla/ThrottledEventQueue.h"
42 using namespace mozilla
;
43 using mozilla::DebugOnly
;
45 using mozilla::EventDispatcher
;
46 using mozilla::LogLevel
;
47 using mozilla::WidgetEvent
;
48 using mozilla::dom::BrowserChild
;
49 using mozilla::dom::BrowsingContext
;
50 using mozilla::dom::Document
;
53 // Log module for nsIDocumentLoader logging...
55 // To enable logging (see mozilla/Logging.h for full details):
57 // set MOZ_LOG=DocLoader:5
58 // set MOZ_LOG_FILE=debug.log
60 // this enables LogLevel::Debug level information and places all output in
61 // the file 'debug.log'.
63 mozilla::LazyLogModule
gDocLoaderLog("DocLoader");
66 void GetURIStringFromRequest(nsIRequest
* request
, nsACString
& name
) {
68 request
->GetName(name
);
70 name
.AssignLiteral("???");
74 void nsDocLoader::RequestInfoHashInitEntry(PLDHashEntryHdr
* entry
,
76 // Initialize the entry with placement new
77 new (entry
) nsRequestInfo(key
);
80 void nsDocLoader::RequestInfoHashClearEntry(PLDHashTable
* table
,
81 PLDHashEntryHdr
* entry
) {
82 nsRequestInfo
* info
= static_cast<nsRequestInfo
*>(entry
);
83 info
->~nsRequestInfo();
86 // this is used for mListenerInfoList.Contains()
88 class nsDefaultComparator
<nsDocLoader::nsListenerInfo
,
89 nsIWebProgressListener
*> {
91 bool Equals(const nsDocLoader::nsListenerInfo
& aInfo
,
92 nsIWebProgressListener
* const& aListener
) const {
93 nsCOMPtr
<nsIWebProgressListener
> listener
=
94 do_QueryReferent(aInfo
.mWeakListener
);
95 return aListener
== listener
;
99 /* static */ const PLDHashTableOps
nsDocLoader::sRequestInfoHashOps
= {
100 PLDHashTable::HashVoidPtrKeyStub
, PLDHashTable::MatchEntryStub
,
101 PLDHashTable::MoveEntryStub
, nsDocLoader::RequestInfoHashClearEntry
,
102 nsDocLoader::RequestInfoHashInitEntry
};
104 nsDocLoader::nsDocLoader(bool aNotifyAboutBackgroundRequests
)
106 mProgressStateFlags(0),
107 mCurrentSelfProgress(0),
109 mCurrentTotalProgress(0),
110 mMaxTotalProgress(0),
111 mRequestInfoHash(&sRequestInfoHashOps
, sizeof(nsRequestInfo
)),
112 mCompletedTotalProgress(0),
113 mIsLoadingDocument(false),
114 mIsRestoringDocument(false),
115 mDontFlushLayout(false),
116 mIsFlushingLayout(false),
117 mDocumentOpenedButNotLoaded(false),
118 mNotifyAboutBackgroundRequests(aNotifyAboutBackgroundRequests
) {
119 ClearInternalProgress();
121 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
, ("DocLoader:%p: created.\n", this));
124 nsresult
nsDocLoader::SetDocLoaderParent(nsDocLoader
* aParent
) {
129 nsresult
nsDocLoader::Init() {
130 RefPtr
<net::nsLoadGroup
> loadGroup
= new net::nsLoadGroup();
131 nsresult rv
= loadGroup
->Init();
132 if (NS_FAILED(rv
)) return rv
;
134 loadGroup
->SetGroupObserver(this, mNotifyAboutBackgroundRequests
);
136 mLoadGroup
= loadGroup
;
138 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
139 ("DocLoader:%p: load group %p.\n", this, mLoadGroup
.get()));
144 nsresult
nsDocLoader::InitWithBrowsingContext(
145 BrowsingContext
* aBrowsingContext
) {
146 RefPtr
<net::nsLoadGroup
> loadGroup
= new net::nsLoadGroup();
147 if (!aBrowsingContext
->GetRequestContextId()) {
148 return NS_ERROR_NOT_AVAILABLE
;
150 nsresult rv
= loadGroup
->InitWithRequestContextId(
151 aBrowsingContext
->GetRequestContextId());
152 if (NS_FAILED(rv
)) return rv
;
154 loadGroup
->SetGroupObserver(this, mNotifyAboutBackgroundRequests
);
156 mLoadGroup
= loadGroup
;
158 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
159 ("DocLoader:%p: load group %p.\n", this, mLoadGroup
.get()));
164 nsDocLoader::~nsDocLoader() {
166 |ClearWeakReferences()| here is intended to prevent people holding
167 weak references from re-entering this destructor since |QueryReferent()|
168 will |AddRef()| me, and the subsequent |Release()| will try to destroy me.
169 At this point there should be only weak references remaining (otherwise, we
170 wouldn't be getting destroyed).
172 An alternative would be incrementing our refcount (consider it a
173 compressed flag saying "Don't re-destroy."). I haven't yet decided which
176 // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
178 ClearWeakReferences();
182 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
, ("DocLoader:%p: deleted.\n", this));
186 * Implementation of ISupports methods...
188 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocLoader
)
189 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDocLoader
)
191 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocLoader
)
192 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDocumentLoader
)
193 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
194 NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader
)
195 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
196 NS_INTERFACE_MAP_ENTRY(nsIWebProgress
)
197 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink
)
198 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
199 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink
)
200 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority
)
201 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsDocLoader
)
204 NS_IMPL_CYCLE_COLLECTION_WEAK(nsDocLoader
, mChildrenInOnload
)
207 * Implementation of nsIInterfaceRequestor methods...
209 NS_IMETHODIMP
nsDocLoader::GetInterface(const nsIID
& aIID
, void** aSink
) {
210 nsresult rv
= NS_ERROR_NO_INTERFACE
;
212 NS_ENSURE_ARG_POINTER(aSink
);
214 if (aIID
.Equals(NS_GET_IID(nsILoadGroup
))) {
216 NS_IF_ADDREF((nsISupports
*)*aSink
);
219 rv
= QueryInterface(aIID
, aSink
);
226 already_AddRefed
<nsDocLoader
> nsDocLoader::GetAsDocLoader(
227 nsISupports
* aSupports
) {
228 RefPtr
<nsDocLoader
> ret
= do_QueryObject(aSupports
);
233 nsresult
nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader
* aDocLoader
) {
234 nsCOMPtr
<nsIDocumentLoader
> docLoaderService
=
235 components::DocLoader::Service();
236 NS_ENSURE_TRUE(docLoaderService
, NS_ERROR_UNEXPECTED
);
238 RefPtr
<nsDocLoader
> rootDocLoader
= GetAsDocLoader(docLoaderService
);
239 NS_ENSURE_TRUE(rootDocLoader
, NS_ERROR_UNEXPECTED
);
241 return rootDocLoader
->AddChildLoader(aDocLoader
);
244 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
245 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
nsDocLoader::Stop(void) {
248 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
249 ("DocLoader:%p: Stop() called\n", this));
251 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList
, Stop
, ());
254 rv
= mLoadGroup
->CancelWithReason(NS_BINDING_ABORTED
,
255 "nsDocLoader::Stop"_ns
);
258 // Don't report that we're flushing layout so IsBusy returns false after a
260 mIsFlushingLayout
= false;
262 // Clear out mChildrenInOnload. We're not going to fire our onload
263 // anyway at this point, and there's no issue with mChildrenInOnload
264 // after this, since mDocumentRequest will be null after the
265 // DocLoaderIsEmpty() call.
266 mChildrenInOnload
.Clear();
267 nsCOMPtr
<nsIDocShell
> ds
= do_QueryInterface(GetAsSupports(this));
268 Document
* doc
= ds
? ds
->GetExtantDocument() : nullptr;
270 doc
->ClearOOPChildrenLoading();
273 // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
274 // etc, as needed. We could be getting into here from a subframe onload, in
275 // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
276 // happened yet, Canceling the loadgroup did nothing (because it was already
277 // empty), and we're about to start a new load (which is what triggered this
280 // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
281 // we wouldn't need the call here....
283 NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
285 // If Cancelling the load group only had pending subresource requests, then
286 // the group status will still be success, and we would fire the load event.
287 // We want to avoid that when we're aborting the load, so override the status
288 // with an explicit NS_BINDING_ABORTED value.
289 DocLoaderIsEmpty(false, Some(NS_BINDING_ABORTED
));
294 bool nsDocLoader::IsBusy() {
298 // A document loader is busy if either:
300 // 1. One of its children is in the middle of an onload handler. Note that
301 // the handler may have already removed this child from mChildList!
302 // 2. It is currently loading a document and either has parts of it still
303 // loading, or has a busy child docloader.
304 // 3. It's currently flushing layout in DocLoaderIsEmpty().
307 nsCOMPtr
<nsIDocShell
> ds
= do_QueryInterface(GetAsSupports(this));
308 Document
* doc
= ds
? ds
->GetExtantDocument() : nullptr;
309 if (!mChildrenInOnload
.IsEmpty() || (doc
&& doc
->HasOOPChildrenLoading()) ||
314 /* Is this document loader busy? */
315 if (!IsBlockingLoadEvent()) {
319 // Check if any in-process sub-document is awaiting its 'load' event:
321 rv
= mLoadGroup
->IsPending(&busy
);
329 /* check its child document loaders... */
330 uint32_t count
= mChildList
.Length();
331 for (uint32_t i
= 0; i
< count
; i
++) {
332 nsIDocumentLoader
* loader
= ChildAt(i
);
333 // This is a safe cast, because we only put nsDocLoader objects into the
335 if (loader
&& static_cast<nsDocLoader
*>(loader
)->IsBusy()) {
344 nsDocLoader::GetContainer(nsISupports
** aResult
) {
345 NS_ADDREF(*aResult
= static_cast<nsIDocumentLoader
*>(this));
351 nsDocLoader::GetLoadGroup(nsILoadGroup
** aResult
) {
354 if (nullptr == aResult
) {
355 rv
= NS_ERROR_NULL_POINTER
;
357 *aResult
= mLoadGroup
;
358 NS_IF_ADDREF(*aResult
);
363 void nsDocLoader::Destroy() {
366 // Remove the document loader from the parent list of loaders...
368 DebugOnly
<nsresult
> rv
= mParent
->RemoveChildLoader(this);
369 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "RemoveChildLoader failed");
372 // Release all the information about network requests...
373 ClearRequestInfoHash();
375 mListenerInfoList
.Clear();
376 mListenerInfoList
.Compact();
378 mDocumentRequest
= nullptr;
380 if (mLoadGroup
) mLoadGroup
->SetGroupObserver(nullptr);
385 void nsDocLoader::DestroyChildren() {
386 uint32_t count
= mChildList
.Length();
387 // if the doc loader still has children...we need to enumerate the
388 // children and make them null out their back ptr to the parent doc
390 for (uint32_t i
= 0; i
< count
; i
++) {
391 nsIDocumentLoader
* loader
= ChildAt(i
);
394 // This is a safe cast, as we only put nsDocLoader objects into the
396 DebugOnly
<nsresult
> rv
=
397 static_cast<nsDocLoader
*>(loader
)->SetDocLoaderParent(nullptr);
398 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "SetDocLoaderParent failed");
405 nsDocLoader::OnStartRequest(nsIRequest
* request
) {
406 // called each time a request is added to the group.
408 // Some docloaders deal with background requests in their OnStartRequest
409 // override, but here we don't want to do anything with them, so return early.
410 nsLoadFlags loadFlags
= 0;
411 request
->GetLoadFlags(&loadFlags
);
412 if (loadFlags
& nsIRequest::LOAD_BACKGROUND
) {
416 if (MOZ_LOG_TEST(gDocLoaderLog
, LogLevel::Debug
)) {
418 request
->GetName(name
);
421 if (mLoadGroup
) mLoadGroup
->GetActiveCount(&count
);
423 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
424 ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u "
426 this, request
, name
.get(), (mIsLoadingDocument
? "true" : "false"),
430 bool justStartedLoading
= false;
432 if (!mIsLoadingDocument
&& (loadFlags
& nsIChannel::LOAD_DOCUMENT_URI
)) {
433 justStartedLoading
= true;
434 mIsLoadingDocument
= true;
435 mDocumentOpenedButNotLoaded
= false;
436 ClearInternalProgress(); // only clear our progress if we are starting a
441 // Create a new nsRequestInfo for the request that is starting to
444 AddRequestInfo(request
);
447 // Only fire a doStartDocumentLoad(...) if the document loader
448 // has initiated a load... Otherwise, this notification has
449 // resulted from a request being added to the load group.
451 if (mIsLoadingDocument
) {
452 if (loadFlags
& nsIChannel::LOAD_DOCUMENT_URI
) {
454 // Make sure that the document channel is null at this point...
455 // (unless its been redirected)
458 (loadFlags
& nsIChannel::LOAD_REPLACE
) || !(mDocumentRequest
.get()),
459 "Overwriting an existing document channel!");
461 // This request is associated with the entire document...
462 mDocumentRequest
= request
;
463 mLoadGroup
->SetDefaultLoadRequest(request
);
465 // Only fire the start document load notification for the first
466 // document URI... Do not fire it again for redirections
468 if (justStartedLoading
) {
469 // Update the progress status state
470 mProgressStateFlags
= nsIWebProgressListener::STATE_START
;
472 // Fire the start document load notification
473 doStartDocumentLoad();
479 NS_ASSERTION(!mIsLoadingDocument
|| mDocumentRequest
,
480 "mDocumentRequest MUST be set for the duration of a page load!");
482 // This is the only way to catch document request start event after a redirect
483 // has occured without changing inherited Firefox behaviour significantly.
484 // Problem description:
485 // The combination of |STATE_START + STATE_IS_DOCUMENT| is only sent for
486 // initial request (see |doStartDocumentLoad| call above).
487 // And |STATE_REDIRECTING + STATE_IS_DOCUMENT| is sent with old channel, which
488 // makes it impossible to filter by destination URL (see
489 // |AsyncOnChannelRedirect| implementation).
490 // Fixing any of those bugs may cause unpredictable consequences in any part
491 // of the browser, so we just add a custom flag for this exact situation.
492 int32_t extraFlags
= 0;
493 if (mIsLoadingDocument
&& !justStartedLoading
&&
494 (loadFlags
& nsIChannel::LOAD_DOCUMENT_URI
) &&
495 (loadFlags
& nsIChannel::LOAD_REPLACE
)) {
496 extraFlags
= nsIWebProgressListener::STATE_IS_REDIRECTED_DOCUMENT
;
498 doStartURLLoad(request
, extraFlags
);
503 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
504 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
505 nsDocLoader::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatus
) {
506 // Some docloaders deal with background requests in their OnStopRequest
507 // override, but here we don't want to do anything with them, so return early.
509 aRequest
->GetLoadFlags(&lf
);
510 if (lf
& nsIRequest::LOAD_BACKGROUND
) {
516 if (MOZ_LOG_TEST(gDocLoaderLog
, LogLevel::Debug
)) {
518 aRequest
->GetName(name
);
521 if (mLoadGroup
) mLoadGroup
->GetActiveCount(&count
);
523 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
524 ("DocLoader:%p: OnStopRequest[%p](%s) status=%" PRIx32
525 " mIsLoadingDocument=%s, mDocumentOpenedButNotLoaded=%s,"
527 this, aRequest
, name
.get(), static_cast<uint32_t>(aStatus
),
528 (mIsLoadingDocument
? "true" : "false"),
529 (mDocumentOpenedButNotLoaded
? "true" : "false"), count
));
532 bool fireTransferring
= false;
535 // Set the Maximum progress to the same value as the current progress.
536 // Since the URI has finished loading, all the data is there. Also,
537 // this will allow a more accurate estimation of the max progress (in case
538 // the old value was unknown ie. -1)
540 nsRequestInfo
* info
= GetRequestInfo(aRequest
);
542 // Null out mLastStatus now so we don't find it when looking for
543 // status from now on. This destroys the nsStatusInfo and hence
544 // removes it from our list.
545 info
->mLastStatus
= nullptr;
547 int64_t oldMax
= info
->mMaxProgress
;
549 info
->mMaxProgress
= info
->mCurrentProgress
;
552 // If a request whose content-length was previously unknown has just
553 // finished loading, then use this new data to try to calculate a
554 // mMaxSelfProgress...
556 if ((oldMax
< int64_t(0)) && (mMaxSelfProgress
< int64_t(0))) {
557 mMaxSelfProgress
= CalculateMaxProgress();
560 // As we know the total progress of this request now, save it to be part
561 // of CalculateMaxProgress() result. We need to remove the info from the
562 // hash, see bug 480713.
563 mCompletedTotalProgress
+= info
->mMaxProgress
;
566 // Determine whether a STATE_TRANSFERRING notification should be
569 // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
570 // nsRequestInfo::mCurrentProgress are both 0, then the
571 // STATE_TRANSFERRING notification has not been fired yet...
573 if ((oldMax
== 0) && (info
->mCurrentProgress
== 0)) {
574 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
576 // Only fire a TRANSFERRING notification if the request is also a
577 // channel -- data transfer requires a nsIChannel!
580 if (NS_SUCCEEDED(aStatus
)) {
581 fireTransferring
= true;
584 // If the request failed (for any reason other than being
585 // redirected or retargeted), the TRANSFERRING notification can
586 // still be fired if a HTTP connection was established to a server.
588 else if (aStatus
!= NS_BINDING_REDIRECTED
&&
589 aStatus
!= NS_BINDING_RETARGETED
) {
591 // Only if the load has been targeted (see bug 268483)...
593 if (lf
& nsIChannel::LOAD_TARGETED
) {
594 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aRequest
));
596 uint32_t responseCode
;
597 rv
= httpChannel
->GetResponseStatus(&responseCode
);
598 if (NS_SUCCEEDED(rv
)) {
600 // A valid server status indicates that a connection was
601 // established to the server... So, fire the notification
602 // even though a failure occurred later...
604 fireTransferring
= true;
613 if (fireTransferring
) {
614 // Send a STATE_TRANSFERRING notification for the request.
617 flags
= nsIWebProgressListener::STATE_TRANSFERRING
|
618 nsIWebProgressListener::STATE_IS_REQUEST
;
620 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
622 if (mProgressStateFlags
& nsIWebProgressListener::STATE_START
) {
623 mProgressStateFlags
= nsIWebProgressListener::STATE_TRANSFERRING
;
625 // Send STATE_TRANSFERRING for the document too...
626 flags
|= nsIWebProgressListener::STATE_IS_DOCUMENT
;
629 FireOnStateChange(this, aRequest
, flags
, NS_OK
);
633 // Fire the OnStateChange(...) notification for stop request
635 doStopURLLoad(aRequest
, aStatus
);
637 // Clear this request out of the hash to avoid bypass of FireOnStateChange
638 // when address of the request is reused.
639 RemoveRequestInfo(aRequest
);
641 // For the special case where the current document is an initial about:blank
642 // document, we may still have subframes loading, and keeping the DocLoader
643 // busy. In that case, if we have an error, we won't show it until those
644 // frames finish loading, which is nonsensical. So stop any subframe loads
646 if (NS_FAILED(aStatus
) && aStatus
!= NS_BINDING_ABORTED
&&
647 aStatus
!= NS_BINDING_REDIRECTED
&& aStatus
!= NS_BINDING_RETARGETED
) {
648 if (RefPtr
<Document
> doc
= do_GetInterface(GetAsSupports(this))) {
649 if (doc
->IsInitialDocument()) {
650 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList
, Stop
, ());
656 // Only fire the DocLoaderIsEmpty(...) if we may need to fire onload.
658 if (IsBlockingLoadEvent()) {
659 nsCOMPtr
<nsIDocShell
> ds
=
660 do_QueryInterface(static_cast<nsIRequestObserver
*>(this));
661 bool doNotFlushLayout
= false;
663 // Don't do unexpected layout flushes while we're in process of restoring
664 // a document from the bfcache.
665 ds
->GetRestoringDocument(&doNotFlushLayout
);
667 DocLoaderIsEmpty(!doNotFlushLayout
);
673 nsresult
nsDocLoader::RemoveChildLoader(nsDocLoader
* aChild
) {
674 nsresult rv
= mChildList
.RemoveElement(aChild
) ? NS_OK
: NS_ERROR_FAILURE
;
675 if (NS_SUCCEEDED(rv
)) {
676 rv
= aChild
->SetDocLoaderParent(nullptr);
681 nsresult
nsDocLoader::AddChildLoader(nsDocLoader
* aChild
) {
682 mChildList
.AppendElement(aChild
);
683 return aChild
->SetDocLoaderParent(this);
686 NS_IMETHODIMP
nsDocLoader::GetDocumentChannel(nsIChannel
** aChannel
) {
687 if (!mDocumentRequest
) {
692 return CallQueryInterface(mDocumentRequest
, aChannel
);
695 void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout
,
696 const Maybe
<nsresult
>& aOverrideStatus
) {
697 if (IsBlockingLoadEvent()) {
698 /* In the unimagineably rude circumstance that onload event handlers
699 triggered by this function actually kill the window ... ok, it's
700 not unimagineable; it's happened ... this deathgrip keeps this object
701 alive long enough to survive this function call. */
702 nsCOMPtr
<nsIDocumentLoader
> kungFuDeathGrip(this);
704 // Don't flush layout if we're still busy.
709 NS_ASSERTION(!mIsFlushingLayout
, "Someone screwed up");
710 // We may not have a document request if we are in a
711 // document.open() situation.
712 NS_ASSERTION(mDocumentRequest
|| mDocumentOpenedButNotLoaded
,
713 "No Document Request!");
715 // The load group for this DocumentLoader is idle. Flush if we need to.
716 if (aFlushLayout
&& !mDontFlushLayout
) {
717 nsCOMPtr
<Document
> doc
= do_GetInterface(GetAsSupports(this));
719 // We start loads from style resolution, so we need to flush out style
720 // no matter what. If we have user fonts, we also need to flush layout,
721 // since the reflow is what starts font loads.
722 mozilla::FlushType flushType
= mozilla::FlushType::Style
;
723 // Be safe in case this presshell is in teardown now
724 doc
->FlushUserFontSet();
725 if (doc
->GetUserFontSet()) {
726 flushType
= mozilla::FlushType::Layout
;
728 mDontFlushLayout
= mIsFlushingLayout
= true;
729 doc
->FlushPendingNotifications(flushType
);
730 mDontFlushLayout
= mIsFlushingLayout
= false;
734 // And now check whether we're really busy; that might have changed with
737 // Note, mDocumentRequest can be null while mDocumentOpenedButNotLoaded is
738 // false if the flushing above re-entered this method.
739 if (IsBusy() || (!mDocumentRequest
&& !mDocumentOpenedButNotLoaded
)) {
743 if (mDocumentRequest
) {
744 // Clear out our request info hash, now that our load really is done and
745 // we don't need it anymore to CalculateMaxProgress().
746 ClearInternalProgress();
748 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
749 ("DocLoader:%p: Is now idle...\n", this));
751 nsCOMPtr
<nsIRequest
> docRequest
= mDocumentRequest
;
753 mDocumentRequest
= nullptr;
754 mIsLoadingDocument
= false;
756 // Update the progress status state - the document is done
757 mProgressStateFlags
= nsIWebProgressListener::STATE_STOP
;
759 nsresult loadGroupStatus
= NS_OK
;
760 if (aOverrideStatus
) {
761 loadGroupStatus
= *aOverrideStatus
;
763 mLoadGroup
->GetStatus(&loadGroupStatus
);
767 // New code to break the circular reference between
768 // the load group and the docloader...
770 mLoadGroup
->SetDefaultLoadRequest(nullptr);
772 // Take a ref to our parent now so that we can call ChildDoneWithOnload()
773 // on it even if our onload handler removes us from the docloader tree.
774 RefPtr
<nsDocLoader
> parent
= mParent
;
776 // Note that if calling ChildEnteringOnload() on the parent returns false
777 // then calling our onload handler is not safe. That can only happen on
778 // OOM, so that's ok.
779 if (!parent
|| parent
->ChildEnteringOnload(this)) {
780 // Do nothing with our state after firing the
781 // OnEndDocumentLoad(...). The document loader may be loading a *new*
782 // document - if LoadDocument() was called from a handler!
784 doStopDocumentLoad(docRequest
, loadGroupStatus
);
786 NotifyDoneWithOnload(parent
);
789 MOZ_ASSERT(mDocumentOpenedButNotLoaded
);
790 mDocumentOpenedButNotLoaded
= false;
792 // Make sure we do the ChildEnteringOnload/ChildDoneWithOnload even if we
793 // plan to skip firing our own load event, because otherwise we might
794 // never end up firing our parent's load event.
795 RefPtr
<nsDocLoader
> parent
= mParent
;
796 if (!parent
|| parent
->ChildEnteringOnload(this)) {
797 nsresult loadGroupStatus
= NS_OK
;
798 mLoadGroup
->GetStatus(&loadGroupStatus
);
799 // Make sure we're not canceling the loadgroup. If we are, then just
800 // like the normal navigation case we should not fire a load event.
801 if (NS_SUCCEEDED(loadGroupStatus
) ||
802 loadGroupStatus
== NS_ERROR_PARSED_DATA_CACHED
) {
803 // Can "doc" or "window" ever come back null here? Our state machine
804 // is complicated enough I wouldn't bet against it...
805 if (nsCOMPtr
<Document
> doc
= do_GetInterface(GetAsSupports(this))) {
806 doc
->SetReadyStateInternal(Document::READYSTATE_COMPLETE
,
807 /* updateTimingInformation = */ false);
808 doc
->StopDocumentLoad();
810 nsCOMPtr
<nsPIDOMWindowOuter
> window
= doc
->GetWindow();
811 if (window
&& !doc
->SkipLoadEventAfterClose()) {
812 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
813 ("DocLoader:%p: Firing load event for document.open\n",
816 // This is a very cut-down version of
817 // nsDocumentViewer::LoadComplete that doesn't do various things
818 // that are not relevant here because this wasn't an actual
820 WidgetEvent
event(true, eLoad
);
821 event
.mFlags
.mBubbles
= false;
822 event
.mFlags
.mCancelable
= false;
823 // Dispatching to |window|, but using |document| as the target,
826 nsEventStatus unused
= nsEventStatus_eIgnore
;
827 doc
->SetLoadEventFiring(true);
828 // MOZ_KnownLive due to bug 1506441
829 EventDispatcher::Dispatch(
830 MOZ_KnownLive(nsGlobalWindowOuter::Cast(window
)), nullptr,
831 &event
, nullptr, &unused
);
832 doc
->SetLoadEventFiring(false);
834 // Now unsuppress painting on the presshell, if we
835 // haven't done that yet.
836 RefPtr
<PresShell
> presShell
= doc
->GetPresShell();
837 if (presShell
&& !presShell
->IsDestroying()) {
838 presShell
->UnsuppressPainting();
840 if (!presShell
->IsDestroying()) {
841 presShell
->LoadComplete();
847 NotifyDoneWithOnload(parent
);
853 void nsDocLoader::NotifyDoneWithOnload(nsDocLoader
* aParent
) {
855 // In-process parent:
856 aParent
->ChildDoneWithOnload(this);
858 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(this);
862 BrowsingContext
* bc
= nsDocShell::Cast(docShell
)->GetBrowsingContext();
863 if (bc
->IsContentSubframe() && !bc
->GetParentWindowContext()->IsInProcess()) {
864 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(docShell
)) {
865 mozilla::Unused
<< browserChild
->SendMaybeFireEmbedderLoadEvents(
866 dom::EmbedderElementEventType::NoEvent
);
871 void nsDocLoader::doStartDocumentLoad(void) {
873 nsAutoCString buffer
;
875 GetURIStringFromRequest(mDocumentRequest
, buffer
);
877 gDocLoaderLog
, LogLevel::Debug
,
878 ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
880 this, buffer
.get()));
883 // Fire an OnStatus(...) notification STATE_START. This indicates
884 // that the document represented by mDocumentRequest has started to
886 FireOnStateChange(this, mDocumentRequest
,
887 nsIWebProgressListener::STATE_START
|
888 nsIWebProgressListener::STATE_IS_DOCUMENT
|
889 nsIWebProgressListener::STATE_IS_REQUEST
|
890 nsIWebProgressListener::STATE_IS_WINDOW
|
891 nsIWebProgressListener::STATE_IS_NETWORK
,
895 void nsDocLoader::doStartURLLoad(nsIRequest
* request
, int32_t aExtraFlags
) {
897 nsAutoCString buffer
;
899 GetURIStringFromRequest(request
, buffer
);
900 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
901 ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
903 this, buffer
.get()));
906 FireOnStateChange(this, request
,
907 nsIWebProgressListener::STATE_START
|
908 nsIWebProgressListener::STATE_IS_REQUEST
| aExtraFlags
,
912 void nsDocLoader::doStopURLLoad(nsIRequest
* request
, nsresult aStatus
) {
914 nsAutoCString buffer
;
916 GetURIStringFromRequest(request
, buffer
);
917 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
918 ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
919 "\tURI: %s status=%" PRIx32
"\n",
920 this, buffer
.get(), static_cast<uint32_t>(aStatus
)));
923 FireOnStateChange(this, request
,
924 nsIWebProgressListener::STATE_STOP
|
925 nsIWebProgressListener::STATE_IS_REQUEST
,
928 // Fire a status change message for the most recent unfinished
929 // request to make sure that the displayed status is not outdated.
930 if (!mStatusInfoList
.isEmpty()) {
931 nsStatusInfo
* statusInfo
= mStatusInfoList
.getFirst();
932 FireOnStatusChange(this, statusInfo
->mRequest
, statusInfo
->mStatusCode
,
933 statusInfo
->mStatusMessage
.get());
937 void nsDocLoader::doStopDocumentLoad(nsIRequest
* request
, nsresult aStatus
) {
939 nsAutoCString buffer
;
941 GetURIStringFromRequest(request
, buffer
);
942 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
943 ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
944 "\tURI: %s Status=%" PRIx32
"\n",
945 this, buffer
.get(), static_cast<uint32_t>(aStatus
)));
948 // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
949 // Grab our parent chain before doing that so we can still dispatch
950 // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
951 // the onload handlers rearrange the docshell tree.
952 WebProgressList list
;
953 GatherAncestorWebProgresses(list
);
956 // Fire an OnStateChange(...) notification indicating the the
957 // current document has finished loading...
959 int32_t flags
= nsIWebProgressListener::STATE_STOP
|
960 nsIWebProgressListener::STATE_IS_DOCUMENT
;
961 for (uint32_t i
= 0; i
< list
.Length(); ++i
) {
962 list
[i
]->DoFireOnStateChange(this, request
, flags
, aStatus
);
966 // Fire a final OnStateChange(...) notification indicating the the
967 // current document has finished loading...
969 flags
= nsIWebProgressListener::STATE_STOP
|
970 nsIWebProgressListener::STATE_IS_WINDOW
|
971 nsIWebProgressListener::STATE_IS_NETWORK
;
972 for (uint32_t i
= 0; i
< list
.Length(); ++i
) {
973 list
[i
]->DoFireOnStateChange(this, request
, flags
, aStatus
);
977 ////////////////////////////////////////////////////////////////////////////////////
978 // The following section contains support for nsIWebProgress and related stuff
979 ////////////////////////////////////////////////////////////////////////////////////
982 nsDocLoader::AddProgressListener(nsIWebProgressListener
* aListener
,
983 uint32_t aNotifyMask
) {
984 if (mListenerInfoList
.Contains(aListener
)) {
985 // The listener is already registered!
986 return NS_ERROR_FAILURE
;
989 nsWeakPtr listener
= do_GetWeakReference(aListener
);
991 return NS_ERROR_INVALID_ARG
;
994 mListenerInfoList
.AppendElement(nsListenerInfo(listener
, aNotifyMask
));
999 nsDocLoader::RemoveProgressListener(nsIWebProgressListener
* aListener
) {
1000 return mListenerInfoList
.RemoveElement(aListener
) ? NS_OK
: NS_ERROR_FAILURE
;
1004 nsDocLoader::GetBrowsingContextXPCOM(BrowsingContext
** aResult
) {
1009 BrowsingContext
* nsDocLoader::GetBrowsingContext() { return nullptr; }
1012 nsDocLoader::GetDOMWindow(mozIDOMWindowProxy
** aResult
) {
1013 return CallGetInterface(this, aResult
);
1017 nsDocLoader::GetIsTopLevel(bool* aResult
) {
1018 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(this);
1019 *aResult
= docShell
&& docShell
->GetBrowsingContext()->IsTop();
1024 nsDocLoader::GetIsLoadingDocument(bool* aIsLoadingDocument
) {
1025 *aIsLoadingDocument
= mIsLoadingDocument
;
1031 nsDocLoader::GetLoadType(uint32_t* aLoadType
) {
1034 return NS_ERROR_NOT_IMPLEMENTED
;
1038 nsDocLoader::GetTarget(nsIEventTarget
** aTarget
) {
1039 nsCOMPtr
<nsIEventTarget
> target
= GetMainThreadSerialEventTarget();
1040 target
.forget(aTarget
);
1045 nsDocLoader::SetTarget(nsIEventTarget
* aTarget
) {
1046 return NS_ERROR_NOT_IMPLEMENTED
;
1049 int64_t nsDocLoader::GetMaxTotalProgress() {
1050 int64_t newMaxTotal
= 0;
1052 uint32_t count
= mChildList
.Length();
1053 for (uint32_t i
= 0; i
< count
; i
++) {
1054 int64_t individualProgress
= 0;
1055 nsIDocumentLoader
* docloader
= ChildAt(i
);
1057 // Cast is safe since all children are nsDocLoader too
1058 individualProgress
= ((nsDocLoader
*)docloader
)->GetMaxTotalProgress();
1060 if (individualProgress
< int64_t(0)) // if one of the elements doesn't know
1061 // it's size then none of them do
1063 newMaxTotal
= int64_t(-1);
1066 newMaxTotal
+= individualProgress
;
1069 int64_t progress
= -1;
1070 if (mMaxSelfProgress
>= int64_t(0) && newMaxTotal
>= int64_t(0))
1071 progress
= newMaxTotal
+ mMaxSelfProgress
;
1076 ////////////////////////////////////////////////////////////////////////////////////
1077 // The following section contains support for nsIProgressEventSink which is used
1078 // to pass progress and status between the actual request and the doc loader.
1079 // The doc loader then turns around and makes the right web progress calls based
1080 // on this information.
1081 ////////////////////////////////////////////////////////////////////////////////////
1083 NS_IMETHODIMP
nsDocLoader::OnProgress(nsIRequest
* aRequest
, int64_t aProgress
,
1084 int64_t aProgressMax
) {
1085 int64_t progressDelta
= 0;
1088 // Update the RequestInfo entry with the new progress data
1090 if (nsRequestInfo
* info
= GetRequestInfo(aRequest
)) {
1091 // Update info->mCurrentProgress before we call FireOnStateChange,
1092 // since that can make the "info" pointer invalid.
1093 int64_t oldCurrentProgress
= info
->mCurrentProgress
;
1094 progressDelta
= aProgress
- oldCurrentProgress
;
1095 info
->mCurrentProgress
= aProgress
;
1097 // suppress sending STATE_TRANSFERRING if this is upload progress (see bug
1099 if (!info
->mUploading
&& (int64_t(0) == oldCurrentProgress
) &&
1100 (int64_t(0) == info
->mMaxProgress
)) {
1102 // If we receive an OnProgress event from a toplevel channel that the URI
1103 // Loader has not yet targeted, then we must suppress the event. This is
1104 // necessary to ensure that webprogresslisteners do not get confused when
1105 // the channel is finally targeted. See bug 257308.
1108 aRequest
->GetLoadFlags(&lf
);
1109 if ((lf
& nsIChannel::LOAD_DOCUMENT_URI
) &&
1110 !(lf
& nsIChannel::LOAD_TARGETED
)) {
1112 gDocLoaderLog
, LogLevel::Debug
,
1113 ("DocLoader:%p Ignoring OnProgress while load is not targeted\n",
1119 // This is the first progress notification for the entry. If
1120 // (aMaxProgress != -1) then the content-length of the data is known,
1121 // so update mMaxSelfProgress... Otherwise, set it to -1 to indicate
1122 // that the content-length is no longer known.
1124 if (aProgressMax
!= -1) {
1125 mMaxSelfProgress
+= aProgressMax
;
1126 info
->mMaxProgress
= aProgressMax
;
1128 mMaxSelfProgress
= int64_t(-1);
1129 info
->mMaxProgress
= int64_t(-1);
1132 // Send a STATE_TRANSFERRING notification for the request.
1135 flags
= nsIWebProgressListener::STATE_TRANSFERRING
|
1136 nsIWebProgressListener::STATE_IS_REQUEST
;
1138 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
1140 if (mProgressStateFlags
& nsIWebProgressListener::STATE_START
) {
1141 mProgressStateFlags
= nsIWebProgressListener::STATE_TRANSFERRING
;
1143 // Send STATE_TRANSFERRING for the document too...
1144 flags
|= nsIWebProgressListener::STATE_IS_DOCUMENT
;
1147 FireOnStateChange(this, aRequest
, flags
, NS_OK
);
1150 // Update our overall current progress count.
1151 mCurrentSelfProgress
+= progressDelta
;
1154 // The request is not part of the load group, so ignore its progress
1159 nsAutoCString buffer
;
1161 GetURIStringFromRequest(aRequest
, buffer
);
1163 gDocLoaderLog
, LogLevel::Debug
,
1164 ("DocLoader:%p OOPS - No Request Info for: %s\n", this, buffer
.get()));
1171 // Fire progress notifications out to any registered nsIWebProgressListeners
1173 FireOnProgressChange(this, aRequest
, aProgress
, aProgressMax
, progressDelta
,
1174 mCurrentTotalProgress
, mMaxTotalProgress
);
1179 NS_IMETHODIMP
nsDocLoader::OnStatus(nsIRequest
* aRequest
, nsresult aStatus
,
1180 const char16_t
* aStatusArg
) {
1182 // Fire progress notifications out to any registered nsIWebProgressListeners
1184 if (aStatus
!= NS_OK
) {
1185 // Remember the current status for this request
1186 nsRequestInfo
* info
;
1187 info
= GetRequestInfo(aRequest
);
1189 bool uploading
= (aStatus
== NS_NET_STATUS_WRITING
||
1190 aStatus
== NS_NET_STATUS_SENDING_TO
);
1191 // If switching from uploading to downloading (or vice versa), then we
1192 // need to reset our progress counts. This is designed with HTTP form
1193 // submission in mind, where an upload is performed followed by download
1194 // of possibly several documents.
1195 if (info
->mUploading
!= uploading
) {
1196 mCurrentSelfProgress
= mMaxSelfProgress
= 0;
1197 mCurrentTotalProgress
= mMaxTotalProgress
= 0;
1198 mCompletedTotalProgress
= 0;
1199 info
->mUploading
= uploading
;
1200 info
->mCurrentProgress
= 0;
1201 info
->mMaxProgress
= 0;
1205 nsCOMPtr
<nsIStringBundleService
> sbs
=
1206 mozilla::components::StringBundle::Service();
1207 if (!sbs
) return NS_ERROR_FAILURE
;
1209 nsresult rv
= sbs
->FormatStatusMessage(aStatus
, aStatusArg
, msg
);
1210 if (NS_FAILED(rv
)) return rv
;
1212 // Keep around the message. In case a request finishes, we need to make sure
1213 // to send the status message of another request to our user to that we
1214 // don't display, for example, "Transferring" messages for requests that are
1217 if (!info
->mLastStatus
) {
1218 info
->mLastStatus
= MakeUnique
<nsStatusInfo
>(aRequest
);
1220 // We're going to move it to the front of the list, so remove
1221 // it from wherever it is now.
1222 info
->mLastStatus
->remove();
1224 info
->mLastStatus
->mStatusMessage
= msg
;
1225 info
->mLastStatus
->mStatusCode
= aStatus
;
1226 // Put the info at the front of the list
1227 mStatusInfoList
.insertFront(info
->mLastStatus
.get());
1229 FireOnStatusChange(this, aRequest
, aStatus
, msg
.get());
1234 void nsDocLoader::ClearInternalProgress() {
1235 ClearRequestInfoHash();
1237 mCurrentSelfProgress
= mMaxSelfProgress
= 0;
1238 mCurrentTotalProgress
= mMaxTotalProgress
= 0;
1239 mCompletedTotalProgress
= 0;
1241 mProgressStateFlags
= nsIWebProgressListener::STATE_STOP
;
1245 * |_code| is executed for every listener matching |_flag|
1246 * |listener| should be used inside |_code| as the nsIWebProgressListener var.
1248 #define NOTIFY_LISTENERS(_flag, _code) \
1250 nsCOMPtr<nsIWebProgressListener> listener; \
1251 ListenerArray::BackwardIterator iter(mListenerInfoList); \
1252 while (iter.HasMore()) { \
1253 nsListenerInfo& info = iter.GetNext(); \
1254 if (!(info.mNotifyMask & (_flag))) { \
1257 listener = do_QueryReferent(info.mWeakListener); \
1264 mListenerInfoList.Compact(); \
1267 void nsDocLoader::FireOnProgressChange(nsDocLoader
* aLoadInitiator
,
1268 nsIRequest
* request
, int64_t aProgress
,
1269 int64_t aProgressMax
,
1270 int64_t aProgressDelta
,
1271 int64_t aTotalProgress
,
1272 int64_t aMaxTotalProgress
) {
1273 if (mIsLoadingDocument
) {
1274 mCurrentTotalProgress
+= aProgressDelta
;
1275 mMaxTotalProgress
= GetMaxTotalProgress();
1277 aTotalProgress
= mCurrentTotalProgress
;
1278 aMaxTotalProgress
= mMaxTotalProgress
;
1282 nsAutoCString buffer
;
1284 GetURIStringFromRequest(request
, buffer
);
1285 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
1286 ("DocLoader:%p: Progress (%s): curSelf: %" PRId64
" maxSelf: %" PRId64
1287 " curTotal: %" PRId64
" maxTotal %" PRId64
"\n",
1288 this, buffer
.get(), aProgress
, aProgressMax
, aTotalProgress
,
1289 aMaxTotalProgress
));
1293 nsIWebProgress::NOTIFY_PROGRESS
,
1294 // XXX truncates 64-bit to 32-bit
1295 listener
->OnProgressChange(aLoadInitiator
, request
, int32_t(aProgress
),
1296 int32_t(aProgressMax
), int32_t(aTotalProgress
),
1297 int32_t(aMaxTotalProgress
)););
1299 // Pass the notification up to the parent...
1301 mParent
->FireOnProgressChange(aLoadInitiator
, request
, aProgress
,
1302 aProgressMax
, aProgressDelta
, aTotalProgress
,
1307 void nsDocLoader::GatherAncestorWebProgresses(WebProgressList
& aList
) {
1308 for (nsDocLoader
* loader
= this; loader
; loader
= loader
->mParent
) {
1309 aList
.AppendElement(loader
);
1313 void nsDocLoader::FireOnStateChange(nsIWebProgress
* aProgress
,
1314 nsIRequest
* aRequest
, int32_t aStateFlags
,
1316 WebProgressList list
;
1317 GatherAncestorWebProgresses(list
);
1318 for (uint32_t i
= 0; i
< list
.Length(); ++i
) {
1319 list
[i
]->DoFireOnStateChange(aProgress
, aRequest
, aStateFlags
, aStatus
);
1323 void nsDocLoader::DoFireOnStateChange(nsIWebProgress
* const aProgress
,
1324 nsIRequest
* const aRequest
,
1325 int32_t& aStateFlags
,
1326 const nsresult aStatus
) {
1328 // Remove the STATE_IS_NETWORK bit if necessary.
1330 // The rule is to remove this bit, if the notification has been passed
1331 // up from a child WebProgress, and the current WebProgress is already
1334 if (mIsLoadingDocument
&&
1335 (aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
) &&
1336 (this != aProgress
)) {
1337 aStateFlags
&= ~nsIWebProgressListener::STATE_IS_NETWORK
;
1340 // Add the STATE_RESTORING bit if necessary.
1341 if (mIsRestoringDocument
)
1342 aStateFlags
|= nsIWebProgressListener::STATE_RESTORING
;
1345 nsAutoCString buffer
;
1347 GetURIStringFromRequest(aRequest
, buffer
);
1348 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
1349 ("DocLoader:%p: Status (%s): code: %x\n", this, buffer
.get(),
1353 NS_ASSERTION(aRequest
,
1354 "Firing OnStateChange(...) notification with a NULL request!");
1357 ((aStateFlags
>> 16) & nsIWebProgress::NOTIFY_STATE_ALL
),
1358 listener
->OnStateChange(aProgress
, aRequest
, aStateFlags
, aStatus
););
1361 void nsDocLoader::FireOnLocationChange(nsIWebProgress
* aWebProgress
,
1362 nsIRequest
* aRequest
, nsIURI
* aUri
,
1365 nsIWebProgress::NOTIFY_LOCATION
,
1366 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
1367 ("DocLoader [%p] calling %p->OnLocationChange to %s %x", this,
1368 listener
.get(), aUri
->GetSpecOrDefault().get(), aFlags
));
1369 listener
->OnLocationChange(aWebProgress
, aRequest
, aUri
, aFlags
););
1371 // Pass the notification up to the parent...
1373 mParent
->FireOnLocationChange(aWebProgress
, aRequest
, aUri
, aFlags
);
1377 void nsDocLoader::FireOnStatusChange(nsIWebProgress
* aWebProgress
,
1378 nsIRequest
* aRequest
, nsresult aStatus
,
1379 const char16_t
* aMessage
) {
1381 nsIWebProgress::NOTIFY_STATUS
,
1382 listener
->OnStatusChange(aWebProgress
, aRequest
, aStatus
, aMessage
););
1384 // Pass the notification up to the parent...
1386 mParent
->FireOnStatusChange(aWebProgress
, aRequest
, aStatus
, aMessage
);
1390 bool nsDocLoader::RefreshAttempted(nsIWebProgress
* aWebProgress
, nsIURI
* aURI
,
1391 uint32_t aDelay
, bool aSameURI
) {
1393 * Returns true if the refresh may proceed,
1394 * false if the refresh should be blocked.
1396 bool allowRefresh
= true;
1399 nsIWebProgress::NOTIFY_REFRESH
,
1400 nsCOMPtr
<nsIWebProgressListener2
> listener2
=
1401 do_QueryReferent(info
.mWeakListener
);
1402 if (!listener2
) continue;
1404 bool listenerAllowedRefresh
;
1405 nsresult listenerRV
= listener2
->OnRefreshAttempted(
1406 aWebProgress
, aURI
, aDelay
, aSameURI
, &listenerAllowedRefresh
);
1407 if (NS_FAILED(listenerRV
)) continue;
1409 allowRefresh
= allowRefresh
&& listenerAllowedRefresh
;);
1411 // Pass the notification up to the parent...
1413 allowRefresh
= allowRefresh
&& mParent
->RefreshAttempted(aWebProgress
, aURI
,
1417 return allowRefresh
;
1420 nsresult
nsDocLoader::AddRequestInfo(nsIRequest
* aRequest
) {
1421 if (!mRequestInfoHash
.Add(aRequest
, mozilla::fallible
)) {
1422 return NS_ERROR_OUT_OF_MEMORY
;
1428 void nsDocLoader::RemoveRequestInfo(nsIRequest
* aRequest
) {
1429 mRequestInfoHash
.Remove(aRequest
);
1432 nsDocLoader::nsRequestInfo
* nsDocLoader::GetRequestInfo(
1433 nsIRequest
* aRequest
) const {
1434 return static_cast<nsRequestInfo
*>(mRequestInfoHash
.Search(aRequest
));
1437 void nsDocLoader::ClearRequestInfoHash(void) { mRequestInfoHash
.Clear(); }
1439 int64_t nsDocLoader::CalculateMaxProgress() {
1440 int64_t max
= mCompletedTotalProgress
;
1441 for (auto iter
= mRequestInfoHash
.Iter(); !iter
.Done(); iter
.Next()) {
1442 auto info
= static_cast<const nsRequestInfo
*>(iter
.Get());
1444 if (info
->mMaxProgress
< info
->mCurrentProgress
) {
1447 max
+= info
->mMaxProgress
;
1452 NS_IMETHODIMP
nsDocLoader::AsyncOnChannelRedirect(
1453 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
1454 nsIAsyncVerifyRedirectCallback
* cb
) {
1456 nsLoadFlags loadFlags
= 0;
1457 int32_t stateFlags
= nsIWebProgressListener::STATE_REDIRECTING
|
1458 nsIWebProgressListener::STATE_IS_REQUEST
;
1460 aOldChannel
->GetLoadFlags(&loadFlags
);
1461 // If the document channel is being redirected, then indicate that the
1462 // document is being redirected in the notification...
1463 if (loadFlags
& nsIChannel::LOAD_DOCUMENT_URI
) {
1464 stateFlags
|= nsIWebProgressListener::STATE_IS_DOCUMENT
;
1467 // We only set mDocumentRequest in OnStartRequest(), but its possible
1468 // to get a redirect before that for service worker interception.
1469 if (mDocumentRequest
) {
1470 nsCOMPtr
<nsIRequest
> request(aOldChannel
);
1471 NS_ASSERTION(request
== mDocumentRequest
, "Wrong Document Channel");
1476 OnRedirectStateChange(aOldChannel
, aNewChannel
, aFlags
, stateFlags
);
1477 FireOnStateChange(this, aOldChannel
, stateFlags
, NS_OK
);
1480 cb
->OnRedirectVerifyCallback(NS_OK
);
1484 void nsDocLoader::OnSecurityChange(nsISupports
* aContext
, uint32_t aState
) {
1486 // Fire progress notifications out to any registered nsIWebProgressListeners.
1489 nsCOMPtr
<nsIRequest
> request
= do_QueryInterface(aContext
);
1490 nsIWebProgress
* webProgress
= static_cast<nsIWebProgress
*>(this);
1492 NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_SECURITY
,
1493 listener
->OnSecurityChange(webProgress
, request
, aState
););
1495 // Pass the notification up to the parent...
1497 mParent
->OnSecurityChange(aContext
, aState
);
1502 * Implementation of nsISupportsPriority methods...
1504 * The priority of the DocLoader _is_ the priority of its LoadGroup.
1506 * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
1510 NS_IMETHODIMP
nsDocLoader::GetPriority(int32_t* aPriority
) {
1511 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(mLoadGroup
);
1512 if (p
) return p
->GetPriority(aPriority
);
1518 NS_IMETHODIMP
nsDocLoader::SetPriority(int32_t aPriority
) {
1519 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
1520 ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority
));
1522 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(mLoadGroup
);
1523 if (p
) p
->SetPriority(aPriority
);
1525 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList
, SetPriority
,
1531 NS_IMETHODIMP
nsDocLoader::AdjustPriority(int32_t aDelta
) {
1532 MOZ_LOG(gDocLoaderLog
, LogLevel::Debug
,
1533 ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta
));
1535 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(mLoadGroup
);
1536 if (p
) p
->AdjustPriority(aDelta
);
1538 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList
, AdjustPriority
,
1545 nsDocLoader::GetDocumentRequest(nsIRequest
** aRequest
) {
1546 NS_IF_ADDREF(*aRequest
= mDocumentRequest
);
1551 void nsDocLoader::DumpChannelInfo()
1553 nsChannelInfo
*info
;
1555 int32_t current
=0, max
=0;
1558 printf("==== DocLoader=%x\n", this);
1560 count
= mChannelInfoList
.Count();
1561 for(i
=0; i
<count
; i
++) {
1562 info
= (nsChannelInfo
*)mChannelInfoList
.ElementAt(i
);
1565 nsAutoCString buffer
;
1566 nsresult rv
= NS_OK
;
1568 rv
= info
->mURI
->GetSpec(buffer
);
1571 printf(" [%d] current=%d max=%d [%s]\n", i
,
1572 info
->mCurrentProgress
,
1573 info
->mMaxProgress
, buffer
.get());
1576 current
+= info
->mCurrentProgress
;
1578 if (info
->mMaxProgress
< info
->mCurrentProgress
) {
1581 max
+= info
->mMaxProgress
;
1586 printf("\nCurrent=%d Total=%d\n====\n", current
, max
);