Bug 1890689 remove DynamicResampler::mSetBufferDuration r=pehrsons
[gecko.git] / uriloader / base / nsDocLoader.cpp
blobfe72a2715da8846146377e719559c16e6ef1f7ff
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nspr.h"
7 #include "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"
24 #include "nsString.h"
26 #include "nsCOMPtr.h"
27 #include "nscore.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;
44 using mozilla::eLoad;
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");
65 #if defined(DEBUG)
66 void GetURIStringFromRequest(nsIRequest* request, nsACString& name) {
67 if (request)
68 request->GetName(name);
69 else
70 name.AssignLiteral("???");
72 #endif /* DEBUG */
74 void nsDocLoader::RequestInfoHashInitEntry(PLDHashEntryHdr* entry,
75 const void* key) {
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()
87 template <>
88 class nsDefaultComparator<nsDocLoader::nsListenerInfo,
89 nsIWebProgressListener*> {
90 public:
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)
105 : mParent(nullptr),
106 mProgressStateFlags(0),
107 mCurrentSelfProgress(0),
108 mMaxSelfProgress(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) {
125 mParent = aParent;
126 return NS_OK;
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()));
141 return NS_OK;
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()));
161 return NS_OK;
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
174 is better. [scc]
176 // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
177 // this needed?
178 ClearWeakReferences();
180 Destroy();
182 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)
202 NS_INTERFACE_MAP_END
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))) {
215 *aSink = mLoadGroup;
216 NS_IF_ADDREF((nsISupports*)*aSink);
217 rv = NS_OK;
218 } else {
219 rv = QueryInterface(aIID, aSink);
222 return rv;
225 /* static */
226 already_AddRefed<nsDocLoader> nsDocLoader::GetAsDocLoader(
227 nsISupports* aSupports) {
228 RefPtr<nsDocLoader> ret = do_QueryObject(aSupports);
229 return ret.forget();
232 /* static */
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) {
246 nsresult rv = NS_OK;
248 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
249 ("DocLoader:%p: Stop() called\n", this));
251 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, Stop, ());
253 if (mLoadGroup) {
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
259 // Stop call.
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;
269 if (doc) {
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
278 // Stop() call).
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));
291 return rv;
294 bool nsDocLoader::IsBusy() {
295 nsresult rv;
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()) ||
310 mIsFlushingLayout) {
311 return true;
314 /* Is this document loader busy? */
315 if (!IsBlockingLoadEvent()) {
316 return false;
319 // Check if any in-process sub-document is awaiting its 'load' event:
320 bool busy;
321 rv = mLoadGroup->IsPending(&busy);
322 if (NS_FAILED(rv)) {
323 return false;
325 if (busy) {
326 return true;
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
334 // array
335 if (loader && static_cast<nsDocLoader*>(loader)->IsBusy()) {
336 return true;
340 return false;
343 NS_IMETHODIMP
344 nsDocLoader::GetContainer(nsISupports** aResult) {
345 NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
347 return NS_OK;
350 NS_IMETHODIMP
351 nsDocLoader::GetLoadGroup(nsILoadGroup** aResult) {
352 nsresult rv = NS_OK;
354 if (nullptr == aResult) {
355 rv = NS_ERROR_NULL_POINTER;
356 } else {
357 *aResult = mLoadGroup;
358 NS_IF_ADDREF(*aResult);
360 return rv;
363 void nsDocLoader::Destroy() {
364 Stop();
366 // Remove the document loader from the parent list of loaders...
367 if (mParent) {
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);
382 DestroyChildren();
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
389 // loader
390 for (uint32_t i = 0; i < count; i++) {
391 nsIDocumentLoader* loader = ChildAt(i);
393 if (loader) {
394 // This is a safe cast, as we only put nsDocLoader objects into the
395 // array
396 DebugOnly<nsresult> rv =
397 static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nullptr);
398 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetDocLoaderParent failed");
401 mChildList.Clear();
404 NS_IMETHODIMP
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) {
413 return NS_OK;
416 if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
417 nsAutoCString name;
418 request->GetName(name);
420 uint32_t count = 0;
421 if (mLoadGroup) mLoadGroup->GetActiveCount(&count);
423 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
424 ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u "
425 "active URLs",
426 this, request, name.get(), (mIsLoadingDocument ? "true" : "false"),
427 count));
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
437 // new load....
441 // Create a new nsRequestInfo for the request that is starting to
442 // load...
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)
457 NS_ASSERTION(
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();
474 return NS_OK;
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);
500 return NS_OK;
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.
508 nsLoadFlags lf = 0;
509 aRequest->GetLoadFlags(&lf);
510 if (lf & nsIRequest::LOAD_BACKGROUND) {
511 return NS_OK;
514 nsresult rv = NS_OK;
516 if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
517 nsAutoCString name;
518 aRequest->GetName(name);
520 uint32_t count = 0;
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,"
526 " %u active URLs",
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);
541 if (info) {
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
567 // 'synthesized'.
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!
579 if (channel) {
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));
595 if (httpChannel) {
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.
615 int32_t flags;
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
645 // now.
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;
662 if (ds) {
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);
670 return NS_OK;
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);
678 return rv;
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) {
688 *aChannel = nullptr;
689 return NS_OK;
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.
705 if (IsBusy()) {
706 return;
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));
718 if (doc) {
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
735 // the layout flush.
737 // Note, mDocumentRequest can be null while mDocumentOpenedButNotLoaded is
738 // false if the flushing above re-entered this method.
739 if (IsBusy() || (!mDocumentRequest && !mDocumentOpenedButNotLoaded)) {
740 return;
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;
762 } else {
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);
788 } else {
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",
814 this));
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
819 // navigation.
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,
824 // per spec.
825 event.mTarget = doc;
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) {
854 if (aParent) {
855 // In-process parent:
856 aParent->ChildDoneWithOnload(this);
858 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(this);
859 if (!docShell) {
860 return;
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) {
872 #if defined(DEBUG)
873 nsAutoCString buffer;
875 GetURIStringFromRequest(mDocumentRequest, buffer);
876 MOZ_LOG(
877 gDocLoaderLog, LogLevel::Debug,
878 ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
879 "\tURI: %s \n",
880 this, buffer.get()));
881 #endif /* DEBUG */
883 // Fire an OnStatus(...) notification STATE_START. This indicates
884 // that the document represented by mDocumentRequest has started to
885 // load...
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,
892 NS_OK);
895 void nsDocLoader::doStartURLLoad(nsIRequest* request, int32_t aExtraFlags) {
896 #if defined(DEBUG)
897 nsAutoCString buffer;
899 GetURIStringFromRequest(request, buffer);
900 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
901 ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
902 "\tURI: %s\n",
903 this, buffer.get()));
904 #endif /* DEBUG */
906 FireOnStateChange(this, request,
907 nsIWebProgressListener::STATE_START |
908 nsIWebProgressListener::STATE_IS_REQUEST | aExtraFlags,
909 NS_OK);
912 void nsDocLoader::doStopURLLoad(nsIRequest* request, nsresult aStatus) {
913 #if defined(DEBUG)
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)));
921 #endif /* DEBUG */
923 FireOnStateChange(this, request,
924 nsIWebProgressListener::STATE_STOP |
925 nsIWebProgressListener::STATE_IS_REQUEST,
926 aStatus);
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) {
938 #if defined(DEBUG)
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)));
946 #endif /* DEBUG */
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 ////////////////////////////////////////////////////////////////////////////////////
981 NS_IMETHODIMP
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);
990 if (!listener) {
991 return NS_ERROR_INVALID_ARG;
994 mListenerInfoList.AppendElement(nsListenerInfo(listener, aNotifyMask));
995 return NS_OK;
998 NS_IMETHODIMP
999 nsDocLoader::RemoveProgressListener(nsIWebProgressListener* aListener) {
1000 return mListenerInfoList.RemoveElement(aListener) ? NS_OK : NS_ERROR_FAILURE;
1003 NS_IMETHODIMP
1004 nsDocLoader::GetBrowsingContextXPCOM(BrowsingContext** aResult) {
1005 *aResult = nullptr;
1006 return NS_OK;
1009 BrowsingContext* nsDocLoader::GetBrowsingContext() { return nullptr; }
1011 NS_IMETHODIMP
1012 nsDocLoader::GetDOMWindow(mozIDOMWindowProxy** aResult) {
1013 return CallGetInterface(this, aResult);
1016 NS_IMETHODIMP
1017 nsDocLoader::GetIsTopLevel(bool* aResult) {
1018 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(this);
1019 *aResult = docShell && docShell->GetBrowsingContext()->IsTop();
1020 return NS_OK;
1023 NS_IMETHODIMP
1024 nsDocLoader::GetIsLoadingDocument(bool* aIsLoadingDocument) {
1025 *aIsLoadingDocument = mIsLoadingDocument;
1027 return NS_OK;
1030 NS_IMETHODIMP
1031 nsDocLoader::GetLoadType(uint32_t* aLoadType) {
1032 *aLoadType = 0;
1034 return NS_ERROR_NOT_IMPLEMENTED;
1037 NS_IMETHODIMP
1038 nsDocLoader::GetTarget(nsIEventTarget** aTarget) {
1039 nsCOMPtr<nsIEventTarget> target = GetMainThreadSerialEventTarget();
1040 target.forget(aTarget);
1041 return NS_OK;
1044 NS_IMETHODIMP
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);
1056 if (docloader) {
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);
1064 break;
1065 } else
1066 newMaxTotal += individualProgress;
1069 int64_t progress = -1;
1070 if (mMaxSelfProgress >= int64_t(0) && newMaxTotal >= int64_t(0))
1071 progress = newMaxTotal + mMaxSelfProgress;
1073 return progress;
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
1098 // 240053)
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.
1107 nsLoadFlags lf = 0;
1108 aRequest->GetLoadFlags(&lf);
1109 if ((lf & nsIChannel::LOAD_DOCUMENT_URI) &&
1110 !(lf & nsIChannel::LOAD_TARGETED)) {
1111 MOZ_LOG(
1112 gDocLoaderLog, LogLevel::Debug,
1113 ("DocLoader:%p Ignoring OnProgress while load is not targeted\n",
1114 this));
1115 return NS_OK;
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;
1127 } else {
1128 mMaxSelfProgress = int64_t(-1);
1129 info->mMaxProgress = int64_t(-1);
1132 // Send a STATE_TRANSFERRING notification for the request.
1133 int32_t flags;
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
1155 // information...
1157 else {
1158 #if defined(DEBUG)
1159 nsAutoCString buffer;
1161 GetURIStringFromRequest(aRequest, buffer);
1162 MOZ_LOG(
1163 gDocLoaderLog, LogLevel::Debug,
1164 ("DocLoader:%p OOPS - No Request Info for: %s\n", this, buffer.get()));
1165 #endif /* DEBUG */
1167 return NS_OK;
1171 // Fire progress notifications out to any registered nsIWebProgressListeners
1173 FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
1174 mCurrentTotalProgress, mMaxTotalProgress);
1176 return NS_OK;
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);
1188 if (info) {
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;
1208 nsAutoString msg;
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
1215 // already done.
1216 if (info) {
1217 if (!info->mLastStatus) {
1218 info->mLastStatus = MakeUnique<nsStatusInfo>(aRequest);
1219 } else {
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());
1231 return NS_OK;
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) \
1249 PR_BEGIN_MACRO \
1250 nsCOMPtr<nsIWebProgressListener> listener; \
1251 ListenerArray::BackwardIterator iter(mListenerInfoList); \
1252 while (iter.HasMore()) { \
1253 nsListenerInfo& info = iter.GetNext(); \
1254 if (!(info.mNotifyMask & (_flag))) { \
1255 continue; \
1257 listener = do_QueryReferent(info.mWeakListener); \
1258 if (!listener) { \
1259 iter.Remove(); \
1260 continue; \
1262 _code \
1264 mListenerInfoList.Compact(); \
1265 PR_END_MACRO
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;
1281 #if defined(DEBUG)
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));
1290 #endif /* DEBUG */
1292 NOTIFY_LISTENERS(
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...
1300 if (mParent) {
1301 mParent->FireOnProgressChange(aLoadInitiator, request, aProgress,
1302 aProgressMax, aProgressDelta, aTotalProgress,
1303 aMaxTotalProgress);
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,
1315 nsresult aStatus) {
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
1332 // active...
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;
1344 #if defined(DEBUG)
1345 nsAutoCString buffer;
1347 GetURIStringFromRequest(aRequest, buffer);
1348 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
1349 ("DocLoader:%p: Status (%s): code: %x\n", this, buffer.get(),
1350 aStateFlags));
1351 #endif /* DEBUG */
1353 NS_ASSERTION(aRequest,
1354 "Firing OnStateChange(...) notification with a NULL request!");
1356 NOTIFY_LISTENERS(
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,
1363 uint32_t aFlags) {
1364 NOTIFY_LISTENERS(
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...
1372 if (mParent) {
1373 mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
1377 void nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
1378 nsIRequest* aRequest, nsresult aStatus,
1379 const char16_t* aMessage) {
1380 NOTIFY_LISTENERS(
1381 nsIWebProgress::NOTIFY_STATUS,
1382 listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage););
1384 // Pass the notification up to the parent...
1385 if (mParent) {
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;
1398 NOTIFY_LISTENERS(
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...
1412 if (mParent) {
1413 allowRefresh = allowRefresh && mParent->RefreshAttempted(aWebProgress, aURI,
1414 aDelay, aSameURI);
1417 return allowRefresh;
1420 nsresult nsDocLoader::AddRequestInfo(nsIRequest* aRequest) {
1421 if (!mRequestInfoHash.Add(aRequest, mozilla::fallible)) {
1422 return NS_ERROR_OUT_OF_MEMORY;
1425 return NS_OK;
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) {
1445 return int64_t(-1);
1447 max += info->mMaxProgress;
1449 return max;
1452 NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(
1453 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
1454 nsIAsyncVerifyRedirectCallback* cb) {
1455 if (aOldChannel) {
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;
1466 #if defined(DEBUG)
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");
1473 #endif /* DEBUG */
1476 OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
1477 FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
1480 cb->OnRedirectVerifyCallback(NS_OK);
1481 return 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...
1496 if (mParent) {
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
1507 * go away.
1510 NS_IMETHODIMP nsDocLoader::GetPriority(int32_t* aPriority) {
1511 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1512 if (p) return p->GetPriority(aPriority);
1514 *aPriority = 0;
1515 return NS_OK;
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,
1526 (aPriority));
1528 return NS_OK;
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,
1539 (aDelta));
1541 return NS_OK;
1544 NS_IMETHODIMP
1545 nsDocLoader::GetDocumentRequest(nsIRequest** aRequest) {
1546 NS_IF_ADDREF(*aRequest = mDocumentRequest);
1547 return NS_OK;
1550 #if 0
1551 void nsDocLoader::DumpChannelInfo()
1553 nsChannelInfo *info;
1554 int32_t i, count;
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);
1564 # if defined(DEBUG)
1565 nsAutoCString buffer;
1566 nsresult rv = NS_OK;
1567 if (info->mURI) {
1568 rv = info->mURI->GetSpec(buffer);
1571 printf(" [%d] current=%d max=%d [%s]\n", i,
1572 info->mCurrentProgress,
1573 info->mMaxProgress, buffer.get());
1574 # endif /* DEBUG */
1576 current += info->mCurrentProgress;
1577 if (max >= 0) {
1578 if (info->mMaxProgress < info->mCurrentProgress) {
1579 max = -1;
1580 } else {
1581 max += info->mMaxProgress;
1586 printf("\nCurrent=%d Total=%d\n====\n", current, max);
1588 #endif /* 0 */