1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nspr.h"
7 #include "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"
15
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"
23
24 #include "nsString.h"
25
26 #include "nsCOMPtr.h"
27 #include "nscore.h"
28 #include "nsIWeakReferenceUtils.h"
29 #include "nsQueryObject.h"
30
31 #include "nsPIDOMWindow.h"
32 #include "nsGlobalWindow.h"
33
34 #include "nsIStringBundle.h"
35
36 #include "nsIDocShell.h"
37 #include "mozilla/dom/Document.h"
38 #include "mozilla/dom/DocGroup.h"
39 #include "nsPresContext.h"
40 #include "nsIAsyncVerifyRedirectCallback.h"
41 #include "nsIBrowserDOMWindow.h"
42 #include "nsGlobalWindow.h"
43 #include "mozilla/ThrottledEventQueue.h"
44 using namespace mozilla;
45 using mozilla::DebugOnly;
46 using mozilla::eLoad;
47 using mozilla::EventDispatcher;
48 using mozilla::LogLevel;
49 using mozilla::WidgetEvent;
50 using mozilla::dom::BrowserChild;
51 using mozilla::dom::BrowsingContext;
52 using mozilla::dom::Document;
53
54 //
55 // Log module for nsIDocumentLoader logging...
56 //
57 // To enable logging (see mozilla/Logging.h for full details):
58 //
59 // set MOZ_LOG=DocLoader:5
60 // set MOZ_LOG_FILE=debug.log
61 //
62 // this enables LogLevel::Debug level information and places all output in
63 // the file 'debug.log'.
64 //
65 mozilla::LazyLogModule gDocLoaderLog("DocLoader");
66
67 #if defined(DEBUG)
GetURIStringFromRequest(nsIRequest * request,nsACString & name)68 void GetURIStringFromRequest(nsIRequest* request, nsACString& name) {
69 if (request)
70 request->GetName(name);
71 else
72 name.AssignLiteral("???");
73 }
74 #endif /* DEBUG */
75
RequestInfoHashInitEntry(PLDHashEntryHdr * entry,const void * key)76 void nsDocLoader::RequestInfoHashInitEntry(PLDHashEntryHdr* entry,
77 const void* key) {
78 // Initialize the entry with placement new
79 new (entry) nsRequestInfo(key);
80 }
81
RequestInfoHashClearEntry(PLDHashTable * table,PLDHashEntryHdr * entry)82 void nsDocLoader::RequestInfoHashClearEntry(PLDHashTable* table,
83 PLDHashEntryHdr* entry) {
84 nsRequestInfo* info = static_cast<nsRequestInfo*>(entry);
85 info->~nsRequestInfo();
86 }
87
88 // this is used for mListenerInfoList.Contains()
89 template <>
90 class nsDefaultComparator<nsDocLoader::nsListenerInfo,
91 nsIWebProgressListener*> {
92 public:
Equals(const nsDocLoader::nsListenerInfo & aInfo,nsIWebProgressListener * const & aListener) const93 bool Equals(const nsDocLoader::nsListenerInfo& aInfo,
94 nsIWebProgressListener* const& aListener) const {
95 nsCOMPtr<nsIWebProgressListener> listener =
96 do_QueryReferent(aInfo.mWeakListener);
97 return aListener == listener;
98 }
99 };
100
101 /* static */ const PLDHashTableOps nsDocLoader::sRequestInfoHashOps = {
102 PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
103 PLDHashTable::MoveEntryStub, nsDocLoader::RequestInfoHashClearEntry,
104 nsDocLoader::RequestInfoHashInitEntry};
105
nsDocLoader()106 nsDocLoader::nsDocLoader()
107 : mParent(nullptr),
108 mProgressStateFlags(0),
109 mCurrentSelfProgress(0),
110 mMaxSelfProgress(0),
111 mCurrentTotalProgress(0),
112 mMaxTotalProgress(0),
113 mRequestInfoHash(&sRequestInfoHashOps, sizeof(nsRequestInfo)),
114 mCompletedTotalProgress(0),
115 mIsLoadingDocument(false),
116 mIsRestoringDocument(false),
117 mDontFlushLayout(false),
118 mIsFlushingLayout(false),
119 mTreatAsBackgroundLoad(false),
120 mHasFakeOnLoadDispatched(false),
121 mIsReadyToHandlePostMessage(false),
122 mDocumentOpenedButNotLoaded(false) {
123 ClearInternalProgress();
124
125 MOZ_LOG(gDocLoaderLog, LogLevel::Debug, ("DocLoader:%p: created.\n", this));
126 }
127
SetDocLoaderParent(nsDocLoader * aParent)128 nsresult nsDocLoader::SetDocLoaderParent(nsDocLoader* aParent) {
129 mParent = aParent;
130 return NS_OK;
131 }
132
Init()133 nsresult nsDocLoader::Init() {
134 nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
135 if (NS_FAILED(rv)) return rv;
136
137 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
138 ("DocLoader:%p: load group %p.\n", this, mLoadGroup.get()));
139
140 return NS_OK;
141 }
142
InitWithBrowsingContext(BrowsingContext * aBrowsingContext)143 nsresult nsDocLoader::InitWithBrowsingContext(
144 BrowsingContext* aBrowsingContext) {
145 RefPtr<net::nsLoadGroup> loadGroup = new net::nsLoadGroup();
146 if (!aBrowsingContext->GetRequestContextId()) {
147 return NS_ERROR_NOT_AVAILABLE;
148 }
149 nsresult rv = loadGroup->InitWithRequestContextId(
150 aBrowsingContext->GetRequestContextId());
151 if (NS_FAILED(rv)) return rv;
152
153 rv = loadGroup->SetGroupObserver(this);
154 if (NS_FAILED(rv)) return rv;
155
156 mLoadGroup = loadGroup;
157
158 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
159 ("DocLoader:%p: load group %p.\n", this, mLoadGroup.get()));
160
161 return NS_OK;
162 }
163
~nsDocLoader()164 nsDocLoader::~nsDocLoader() {
165 /*
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).
171
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]
175 */
176 // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
177 // this needed?
178 ClearWeakReferences();
179
180 Destroy();
181
182 MOZ_LOG(gDocLoaderLog, LogLevel::Debug, ("DocLoader:%p: deleted.\n", this));
183 }
184
185 /*
186 * Implementation of ISupports methods...
187 */
188 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDocLoader)189 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDocLoader)
190
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
203
204 NS_IMPL_CYCLE_COLLECTION_WEAK(nsDocLoader, mChildrenInOnload)
205
206 /*
207 * Implementation of nsIInterfaceRequestor methods...
208 */
209 NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink) {
210 nsresult rv = NS_ERROR_NO_INTERFACE;
211
212 NS_ENSURE_ARG_POINTER(aSink);
213
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);
220 }
221
222 return rv;
223 }
224
225 /* static */
GetAsDocLoader(nsISupports * aSupports)226 already_AddRefed<nsDocLoader> nsDocLoader::GetAsDocLoader(
227 nsISupports* aSupports) {
228 RefPtr<nsDocLoader> ret = do_QueryObject(aSupports);
229 return ret.forget();
230 }
231
232 /* static */
AddDocLoaderAsChildOfRoot(nsDocLoader * aDocLoader)233 nsresult nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader) {
234 nsCOMPtr<nsIDocumentLoader> docLoaderService =
235 components::DocLoader::Service();
236 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
237
238 RefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
239 NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
240
241 return rootDocLoader->AddChildLoader(aDocLoader);
242 }
243
244 NS_IMETHODIMP
Stop(void)245 nsDocLoader::Stop(void) {
246 nsresult rv = NS_OK;
247
248 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
249 ("DocLoader:%p: Stop() called\n", this));
250
251 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader, Stop, ());
252
253 if (mLoadGroup) rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
254
255 // Don't report that we're flushing layout so IsBusy returns false after a
256 // Stop call.
257 mIsFlushingLayout = false;
258
259 // Clear out mChildrenInOnload. We want to make sure to fire our
260 // onload at this point, and there's no issue with mChildrenInOnload
261 // after this, since mDocumentRequest will be null after the
262 // DocLoaderIsEmpty() call.
263 mChildrenInOnload.Clear();
264 mOOPChildrenLoading.Clear();
265
266 // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
267 // etc, as needed. We could be getting into here from a subframe onload, in
268 // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
269 // happened yet, Canceling the loadgroup did nothing (because it was already
270 // empty), and we're about to start a new load (which is what triggered this
271 // Stop() call).
272
273 // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
274 // we wouldn't need the call here....
275
276 NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
277 DocLoaderIsEmpty(false);
278
279 return rv;
280 }
281
TreatAsBackgroundLoad()282 bool nsDocLoader::TreatAsBackgroundLoad() { return mTreatAsBackgroundLoad; }
283
SetBackgroundLoadIframe()284 void nsDocLoader::SetBackgroundLoadIframe() { mTreatAsBackgroundLoad = true; }
285
IsBusy()286 bool nsDocLoader::IsBusy() {
287 nsresult rv;
288
289 //
290 // A document loader is busy if either:
291 //
292 // 1. One of its children is in the middle of an onload handler. Note that
293 // the handler may have already removed this child from mChildList!
294 // 2. It is currently loading a document and either has parts of it still
295 // loading, or has a busy child docloader.
296 // 3. It's currently flushing layout in DocLoaderIsEmpty().
297 //
298
299 if (!mChildrenInOnload.IsEmpty() || !mOOPChildrenLoading.IsEmpty() ||
300 mIsFlushingLayout) {
301 return true;
302 }
303
304 /* Is this document loader busy? */
305 if (!IsBlockingLoadEvent()) {
306 return false;
307 }
308
309 // Check if any in-process sub-document is awaiting its 'load' event:
310 bool busy;
311 rv = mLoadGroup->IsPending(&busy);
312 if (NS_FAILED(rv)) {
313 return false;
314 }
315 if (busy) {
316 return true;
317 }
318
319 /* check its child document loaders... */
320 uint32_t count = mChildList.Length();
321 for (uint32_t i = 0; i < count; i++) {
322 nsIDocumentLoader* loader = ChildAt(i);
323
324 // If 'dom.cross_origin_iframes_loaded_in_background' is set, the parent
325 // document treats cross domain iframes as background loading frame
326 if (loader && static_cast<nsDocLoader*>(loader)->TreatAsBackgroundLoad()) {
327 continue;
328 }
329 // This is a safe cast, because we only put nsDocLoader objects into the
330 // array
331 if (loader && static_cast<nsDocLoader*>(loader)->IsBusy()) return true;
332 }
333
334 return false;
335 }
336
337 NS_IMETHODIMP
GetContainer(nsISupports ** aResult)338 nsDocLoader::GetContainer(nsISupports** aResult) {
339 NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
340
341 return NS_OK;
342 }
343
344 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** aResult)345 nsDocLoader::GetLoadGroup(nsILoadGroup** aResult) {
346 nsresult rv = NS_OK;
347
348 if (nullptr == aResult) {
349 rv = NS_ERROR_NULL_POINTER;
350 } else {
351 *aResult = mLoadGroup;
352 NS_IF_ADDREF(*aResult);
353 }
354 return rv;
355 }
356
Destroy()357 void nsDocLoader::Destroy() {
358 Stop();
359
360 // Remove the document loader from the parent list of loaders...
361 if (mParent) {
362 DebugOnly<nsresult> rv = mParent->RemoveChildLoader(this);
363 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveChildLoader failed");
364 }
365
366 // Release all the information about network requests...
367 ClearRequestInfoHash();
368
369 mListenerInfoList.Clear();
370 mListenerInfoList.Compact();
371
372 mDocumentRequest = nullptr;
373
374 if (mLoadGroup) mLoadGroup->SetGroupObserver(nullptr);
375
376 DestroyChildren();
377 }
378
DestroyChildren()379 void nsDocLoader::DestroyChildren() {
380 uint32_t count = mChildList.Length();
381 // if the doc loader still has children...we need to enumerate the
382 // children and make them null out their back ptr to the parent doc
383 // loader
384 for (uint32_t i = 0; i < count; i++) {
385 nsIDocumentLoader* loader = ChildAt(i);
386
387 if (loader) {
388 // This is a safe cast, as we only put nsDocLoader objects into the
389 // array
390 DebugOnly<nsresult> rv =
391 static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nullptr);
392 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetDocLoaderParent failed");
393 }
394 }
395 mChildList.Clear();
396 }
397
398 NS_IMETHODIMP
OnStartRequest(nsIRequest * request)399 nsDocLoader::OnStartRequest(nsIRequest* request) {
400 // called each time a request is added to the group.
401
402 if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
403 nsAutoCString name;
404 request->GetName(name);
405
406 uint32_t count = 0;
407 if (mLoadGroup) mLoadGroup->GetActiveCount(&count);
408
409 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
410 ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u "
411 "active URLs",
412 this, request, name.get(), (mIsLoadingDocument ? "true" : "false"),
413 count));
414 }
415
416 bool bJustStartedLoading = false;
417
418 nsLoadFlags loadFlags = 0;
419 request->GetLoadFlags(&loadFlags);
420
421 if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
422 bJustStartedLoading = true;
423 mIsLoadingDocument = true;
424 mDocumentOpenedButNotLoaded = false;
425 ClearInternalProgress(); // only clear our progress if we are starting a
426 // new load....
427 }
428
429 //
430 // Create a new nsRequestInfo for the request that is starting to
431 // load...
432 //
433 AddRequestInfo(request);
434
435 //
436 // Only fire a doStartDocumentLoad(...) if the document loader
437 // has initiated a load... Otherwise, this notification has
438 // resulted from a request being added to the load group.
439 //
440 if (mIsLoadingDocument) {
441 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
442 //
443 // Make sure that the document channel is null at this point...
444 // (unless its been redirected)
445 //
446 NS_ASSERTION(
447 (loadFlags & nsIChannel::LOAD_REPLACE) || !(mDocumentRequest.get()),
448 "Overwriting an existing document channel!");
449
450 // This request is associated with the entire document...
451 mDocumentRequest = request;
452 mLoadGroup->SetDefaultLoadRequest(request);
453
454 // Only fire the start document load notification for the first
455 // document URI... Do not fire it again for redirections
456 //
457 if (bJustStartedLoading) {
458 // Update the progress status state
459 mProgressStateFlags = nsIWebProgressListener::STATE_START;
460
461 // Fire the start document load notification
462 doStartDocumentLoad();
463 return NS_OK;
464 }
465 }
466 }
467
468 NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
469 "mDocumentRequest MUST be set for the duration of a page load!");
470
471 // This is the only way to catch document request start event after a redirect
472 // has occured without changing inherited Firefox behaviour significantly.
473 // Problem description:
474 // The combination of |STATE_START + STATE_IS_DOCUMENT| is only sent for
475 // initial request (see |doStartDocumentLoad| call above).
476 // And |STATE_REDIRECTING + STATE_IS_DOCUMENT| is sent with old channel, which
477 // makes it impossible to filter by destination URL (see
478 // |AsyncOnChannelRedirect| implementation).
479 // Fixing any of those bugs may cause unpredictable consequences in any part
480 // of the browser, so we just add a custom flag for this exact situation.
481 int32_t extraFlags = 0;
482 if (mIsLoadingDocument && !bJustStartedLoading &&
483 (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) &&
484 (loadFlags & nsIChannel::LOAD_REPLACE)) {
485 extraFlags = nsIWebProgressListener::STATE_IS_REDIRECTED_DOCUMENT;
486 }
487 doStartURLLoad(request, extraFlags);
488
489 return NS_OK;
490 }
491
492 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatus)493 nsDocLoader::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
494 nsresult rv = NS_OK;
495
496 if (MOZ_LOG_TEST(gDocLoaderLog, LogLevel::Debug)) {
497 nsAutoCString name;
498 aRequest->GetName(name);
499
500 uint32_t count = 0;
501 if (mLoadGroup) mLoadGroup->GetActiveCount(&count);
502
503 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
504 ("DocLoader:%p: OnStopRequest[%p](%s) status=%" PRIx32
505 " mIsLoadingDocument=%s, mDocumentOpenedButNotLoaded=%s,"
506 " %u active URLs",
507 this, aRequest, name.get(), static_cast<uint32_t>(aStatus),
508 (mIsLoadingDocument ? "true" : "false"),
509 (mDocumentOpenedButNotLoaded ? "true" : "false"), count));
510 }
511
512 bool bFireTransferring = false;
513
514 //
515 // Set the Maximum progress to the same value as the current progress.
516 // Since the URI has finished loading, all the data is there. Also,
517 // this will allow a more accurate estimation of the max progress (in case
518 // the old value was unknown ie. -1)
519 //
520 nsRequestInfo* info = GetRequestInfo(aRequest);
521 if (info) {
522 // Null out mLastStatus now so we don't find it when looking for
523 // status from now on. This destroys the nsStatusInfo and hence
524 // removes it from our list.
525 info->mLastStatus = nullptr;
526
527 int64_t oldMax = info->mMaxProgress;
528
529 info->mMaxProgress = info->mCurrentProgress;
530
531 //
532 // If a request whose content-length was previously unknown has just
533 // finished loading, then use this new data to try to calculate a
534 // mMaxSelfProgress...
535 //
536 if ((oldMax < int64_t(0)) && (mMaxSelfProgress < int64_t(0))) {
537 mMaxSelfProgress = CalculateMaxProgress();
538 }
539
540 // As we know the total progress of this request now, save it to be part
541 // of CalculateMaxProgress() result. We need to remove the info from the
542 // hash, see bug 480713.
543 mCompletedTotalProgress += info->mMaxProgress;
544
545 //
546 // Determine whether a STATE_TRANSFERRING notification should be
547 // 'synthesized'.
548 //
549 // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
550 // nsRequestInfo::mCurrentProgress are both 0, then the
551 // STATE_TRANSFERRING notification has not been fired yet...
552 //
553 if ((oldMax == 0) && (info->mCurrentProgress == 0)) {
554 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
555
556 // Only fire a TRANSFERRING notification if the request is also a
557 // channel -- data transfer requires a nsIChannel!
558 //
559 if (channel) {
560 if (NS_SUCCEEDED(aStatus)) {
561 bFireTransferring = true;
562 }
563 //
564 // If the request failed (for any reason other than being
565 // redirected or retargeted), the TRANSFERRING notification can
566 // still be fired if a HTTP connection was established to a server.
567 //
568 else if (aStatus != NS_BINDING_REDIRECTED &&
569 aStatus != NS_BINDING_RETARGETED) {
570 //
571 // Only if the load has been targeted (see bug 268483)...
572 //
573 uint32_t lf;
574 channel->GetLoadFlags(&lf);
575 if (lf & nsIChannel::LOAD_TARGETED) {
576 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
577 if (httpChannel) {
578 uint32_t responseCode;
579 rv = httpChannel->GetResponseStatus(&responseCode);
580 if (NS_SUCCEEDED(rv)) {
581 //
582 // A valid server status indicates that a connection was
583 // established to the server... So, fire the notification
584 // even though a failure occurred later...
585 //
586 bFireTransferring = true;
587 }
588 }
589 }
590 }
591 }
592 }
593 }
594
595 if (bFireTransferring) {
596 // Send a STATE_TRANSFERRING notification for the request.
597 int32_t flags;
598
599 flags = nsIWebProgressListener::STATE_TRANSFERRING |
600 nsIWebProgressListener::STATE_IS_REQUEST;
601 //
602 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
603 //
604 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
605 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
606
607 // Send STATE_TRANSFERRING for the document too...
608 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
609 }
610
611 FireOnStateChange(this, aRequest, flags, NS_OK);
612 }
613
614 //
615 // Fire the OnStateChange(...) notification for stop request
616 //
617 doStopURLLoad(aRequest, aStatus);
618
619 // Clear this request out of the hash to avoid bypass of FireOnStateChange
620 // when address of the request is reused.
621 RemoveRequestInfo(aRequest);
622
623 //
624 // Only fire the DocLoaderIsEmpty(...) if we may need to fire onload.
625 //
626 if (IsBlockingLoadEvent()) {
627 nsCOMPtr<nsIDocShell> ds =
628 do_QueryInterface(static_cast<nsIRequestObserver*>(this));
629 bool doNotFlushLayout = false;
630 if (ds) {
631 // Don't do unexpected layout flushes while we're in process of restoring
632 // a document from the bfcache.
633 ds->GetRestoringDocument(&doNotFlushLayout);
634 }
635 DocLoaderIsEmpty(!doNotFlushLayout);
636 }
637
638 return NS_OK;
639 }
640
RemoveChildLoader(nsDocLoader * aChild)641 nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild) {
642 nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
643 if (NS_SUCCEEDED(rv)) {
644 rv = aChild->SetDocLoaderParent(nullptr);
645 }
646 return rv;
647 }
648
AddChildLoader(nsDocLoader * aChild)649 nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild) {
650 mChildList.AppendElement(aChild);
651 return aChild->SetDocLoaderParent(this);
652 }
653
GetDocumentChannel(nsIChannel ** aChannel)654 NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel** aChannel) {
655 if (!mDocumentRequest) {
656 *aChannel = nullptr;
657 return NS_OK;
658 }
659
660 return CallQueryInterface(mDocumentRequest, aChannel);
661 }
662
DocLoaderIsEmpty(bool aFlushLayout)663 void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout) {
664 if (IsBlockingLoadEvent()) {
665 /* In the unimagineably rude circumstance that onload event handlers
666 triggered by this function actually kill the window ... ok, it's
667 not unimagineable; it's happened ... this deathgrip keeps this object
668 alive long enough to survive this function call. */
669 nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
670
671 // Don't flush layout if we're still busy.
672 if (IsBusy()) {
673 return;
674 }
675
676 NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
677 // We may not have a document request if we are in a
678 // document.open() situation.
679 NS_ASSERTION(mDocumentRequest || mDocumentOpenedButNotLoaded,
680 "No Document Request!");
681
682 // The load group for this DocumentLoader is idle. Flush if we need to.
683 if (aFlushLayout && !mDontFlushLayout) {
684 nsCOMPtr<Document> doc = do_GetInterface(GetAsSupports(this));
685 if (doc) {
686 // We start loads from style resolution, so we need to flush out style
687 // no matter what. If we have user fonts, we also need to flush layout,
688 // since the reflow is what starts font loads.
689 mozilla::FlushType flushType = mozilla::FlushType::Style;
690 // Be safe in case this presshell is in teardown now
691 doc->FlushUserFontSet();
692 if (doc->GetUserFontSet()) {
693 flushType = mozilla::FlushType::Layout;
694 }
695 mDontFlushLayout = mIsFlushingLayout = true;
696 doc->FlushPendingNotifications(flushType);
697 mDontFlushLayout = mIsFlushingLayout = false;
698 }
699 }
700
701 // And now check whether we're really busy; that might have changed with
702 // the layout flush.
703 //
704 // Note, mDocumentRequest can be null while mDocumentOpenedButNotLoaded is
705 // false if the flushing above re-entered this method.
706 if (IsBusy() || (!mDocumentRequest && !mDocumentOpenedButNotLoaded)) {
707 return;
708 }
709
710 if (mDocumentRequest) {
711 // Clear out our request info hash, now that our load really is done and
712 // we don't need it anymore to CalculateMaxProgress().
713 ClearInternalProgress();
714
715 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
716 ("DocLoader:%p: Is now idle...\n", this));
717
718 nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
719
720 mDocumentRequest = nullptr;
721 mIsLoadingDocument = false;
722
723 // Update the progress status state - the document is done
724 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
725
726 nsresult loadGroupStatus = NS_OK;
727 mLoadGroup->GetStatus(&loadGroupStatus);
728
729 //
730 // New code to break the circular reference between
731 // the load group and the docloader...
732 //
733 mLoadGroup->SetDefaultLoadRequest(nullptr);
734
735 // Take a ref to our parent now so that we can call ChildDoneWithOnload()
736 // on it even if our onload handler removes us from the docloader tree.
737 RefPtr<nsDocLoader> parent = mParent;
738
739 // Note that if calling ChildEnteringOnload() on the parent returns false
740 // then calling our onload handler is not safe. That can only happen on
741 // OOM, so that's ok.
742 if (!parent || parent->ChildEnteringOnload(this)) {
743 // Do nothing with our state after firing the
744 // OnEndDocumentLoad(...). The document loader may be loading a *new*
745 // document - if LoadDocument() was called from a handler!
746 //
747 doStopDocumentLoad(docRequest, loadGroupStatus);
748
749 NotifyDoneWithOnload(parent);
750 }
751 } else {
752 MOZ_ASSERT(mDocumentOpenedButNotLoaded);
753 mDocumentOpenedButNotLoaded = false;
754
755 // Make sure we do the ChildEnteringOnload/ChildDoneWithOnload even if we
756 // plan to skip firing our own load event, because otherwise we might
757 // never end up firing our parent's load event.
758 RefPtr<nsDocLoader> parent = mParent;
759 if (!parent || parent->ChildEnteringOnload(this)) {
760 nsresult loadGroupStatus = NS_OK;
761 mLoadGroup->GetStatus(&loadGroupStatus);
762 // Make sure we're not canceling the loadgroup. If we are, then just
763 // like the normal navigation case we should not fire a load event.
764 if (NS_SUCCEEDED(loadGroupStatus) ||
765 loadGroupStatus == NS_ERROR_PARSED_DATA_CACHED) {
766 // Can "doc" or "window" ever come back null here? Our state machine
767 // is complicated enough I wouldn't bet against it...
768 nsCOMPtr<Document> doc = do_GetInterface(GetAsSupports(this));
769 if (doc) {
770 doc->SetReadyStateInternal(Document::READYSTATE_COMPLETE,
771 /* updateTimingInformation = */ false);
772
773 nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow();
774 if (window && !doc->SkipLoadEventAfterClose()) {
775 if (!mozilla::dom::DocGroup::TryToLoadIframesInBackground() ||
776 (mozilla::dom::DocGroup::TryToLoadIframesInBackground() &&
777 !HasFakeOnLoadDispatched())) {
778 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
779 ("DocLoader:%p: Firing load event for document.open\n",
780 this));
781
782 // This is a very cut-down version of
783 // nsDocumentViewer::LoadComplete that doesn't do various things
784 // that are not relevant here because this wasn't an actual
785 // navigation.
786 WidgetEvent event(true, eLoad);
787 event.mFlags.mBubbles = false;
788 event.mFlags.mCancelable = false;
789 // Dispatching to |window|, but using |document| as the target,
790 // per spec.
791 event.mTarget = doc;
792 nsEventStatus unused = nsEventStatus_eIgnore;
793 doc->SetLoadEventFiring(true);
794 EventDispatcher::Dispatch(window, nullptr, &event, nullptr,
795 &unused);
796 doc->SetLoadEventFiring(false);
797
798 // Now unsuppress painting on the presshell, if we
799 // haven't done that yet.
800 RefPtr<PresShell> presShell = doc->GetPresShell();
801 if (presShell && !presShell->IsDestroying()) {
802 presShell->UnsuppressPainting();
803
804 if (!presShell->IsDestroying()) {
805 presShell->LoadComplete();
806 }
807 }
808 }
809 }
810 }
811 }
812 NotifyDoneWithOnload(parent);
813 }
814 }
815 }
816 }
817
NotifyDoneWithOnload(nsDocLoader * aParent)818 void nsDocLoader::NotifyDoneWithOnload(nsDocLoader* aParent) {
819 if (aParent) {
820 // In-process parent:
821 aParent->ChildDoneWithOnload(this);
822 }
823 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(this);
824 if (!docShell) {
825 return;
826 }
827 BrowsingContext* bc = nsDocShell::Cast(docShell)->GetBrowsingContext();
828 if (bc->IsContentSubframe() && !bc->GetParent()->IsInProcess()) {
829 if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
830 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
831 dom::EmbedderElementEventType::NoEvent);
832 }
833 }
834 }
835
doStartDocumentLoad(void)836 void nsDocLoader::doStartDocumentLoad(void) {
837 #if defined(DEBUG)
838 nsAutoCString buffer;
839
840 GetURIStringFromRequest(mDocumentRequest, buffer);
841 MOZ_LOG(
842 gDocLoaderLog, LogLevel::Debug,
843 ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
844 "\tURI: %s \n",
845 this, buffer.get()));
846 #endif /* DEBUG */
847
848 // Fire an OnStatus(...) notification STATE_START. This indicates
849 // that the document represented by mDocumentRequest has started to
850 // load...
851 FireOnStateChange(this, mDocumentRequest,
852 nsIWebProgressListener::STATE_START |
853 nsIWebProgressListener::STATE_IS_DOCUMENT |
854 nsIWebProgressListener::STATE_IS_REQUEST |
855 nsIWebProgressListener::STATE_IS_WINDOW |
856 nsIWebProgressListener::STATE_IS_NETWORK,
857 NS_OK);
858 }
859
doStartURLLoad(nsIRequest * request,int32_t aExtraFlags)860 void nsDocLoader::doStartURLLoad(nsIRequest* request, int32_t aExtraFlags) {
861 #if defined(DEBUG)
862 nsAutoCString buffer;
863
864 GetURIStringFromRequest(request, buffer);
865 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
866 ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
867 "\tURI: %s\n",
868 this, buffer.get()));
869 #endif /* DEBUG */
870
871 FireOnStateChange(this, request,
872 nsIWebProgressListener::STATE_START |
873 nsIWebProgressListener::STATE_IS_REQUEST | aExtraFlags,
874 NS_OK);
875 }
876
doStopURLLoad(nsIRequest * request,nsresult aStatus)877 void nsDocLoader::doStopURLLoad(nsIRequest* request, nsresult aStatus) {
878 #if defined(DEBUG)
879 nsAutoCString buffer;
880
881 GetURIStringFromRequest(request, buffer);
882 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
883 ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
884 "\tURI: %s status=%" PRIx32 "\n",
885 this, buffer.get(), static_cast<uint32_t>(aStatus)));
886 #endif /* DEBUG */
887
888 FireOnStateChange(this, request,
889 nsIWebProgressListener::STATE_STOP |
890 nsIWebProgressListener::STATE_IS_REQUEST,
891 aStatus);
892
893 // Fire a status change message for the most recent unfinished
894 // request to make sure that the displayed status is not outdated.
895 if (!mStatusInfoList.isEmpty()) {
896 nsStatusInfo* statusInfo = mStatusInfoList.getFirst();
897 FireOnStatusChange(this, statusInfo->mRequest, statusInfo->mStatusCode,
898 statusInfo->mStatusMessage.get());
899 }
900 }
901
doStopDocumentLoad(nsIRequest * request,nsresult aStatus)902 void nsDocLoader::doStopDocumentLoad(nsIRequest* request, nsresult aStatus) {
903 #if defined(DEBUG)
904 nsAutoCString buffer;
905
906 GetURIStringFromRequest(request, buffer);
907 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
908 ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
909 "\tURI: %s Status=%" PRIx32 "\n",
910 this, buffer.get(), static_cast<uint32_t>(aStatus)));
911 #endif /* DEBUG */
912
913 // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
914 // Grab our parent chain before doing that so we can still dispatch
915 // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
916 // the onload handlers rearrange the docshell tree.
917 WebProgressList list;
918 GatherAncestorWebProgresses(list);
919
920 //
921 // Fire an OnStateChange(...) notification indicating the the
922 // current document has finished loading...
923 //
924 int32_t flags = nsIWebProgressListener::STATE_STOP |
925 nsIWebProgressListener::STATE_IS_DOCUMENT;
926 for (uint32_t i = 0; i < list.Length(); ++i) {
927 list[i]->DoFireOnStateChange(this, request, flags, aStatus);
928 }
929
930 //
931 // Fire a final OnStateChange(...) notification indicating the the
932 // current document has finished loading...
933 //
934 flags = nsIWebProgressListener::STATE_STOP |
935 nsIWebProgressListener::STATE_IS_WINDOW |
936 nsIWebProgressListener::STATE_IS_NETWORK;
937 for (uint32_t i = 0; i < list.Length(); ++i) {
938 list[i]->DoFireOnStateChange(this, request, flags, aStatus);
939 }
940 }
941
942 ////////////////////////////////////////////////////////////////////////////////////
943 // The following section contains support for nsIWebProgress and related stuff
944 ////////////////////////////////////////////////////////////////////////////////////
945
946 NS_IMETHODIMP
AddProgressListener(nsIWebProgressListener * aListener,uint32_t aNotifyMask)947 nsDocLoader::AddProgressListener(nsIWebProgressListener* aListener,
948 uint32_t aNotifyMask) {
949 if (mListenerInfoList.Contains(aListener)) {
950 // The listener is already registered!
951 return NS_ERROR_FAILURE;
952 }
953
954 nsWeakPtr listener = do_GetWeakReference(aListener);
955 if (!listener) {
956 return NS_ERROR_INVALID_ARG;
957 }
958
959 mListenerInfoList.AppendElement(nsListenerInfo(listener, aNotifyMask));
960 return NS_OK;
961 }
962
963 NS_IMETHODIMP
RemoveProgressListener(nsIWebProgressListener * aListener)964 nsDocLoader::RemoveProgressListener(nsIWebProgressListener* aListener) {
965 return mListenerInfoList.RemoveElement(aListener) ? NS_OK : NS_ERROR_FAILURE;
966 }
967
968 NS_IMETHODIMP
GetDOMWindow(mozIDOMWindowProxy ** aResult)969 nsDocLoader::GetDOMWindow(mozIDOMWindowProxy** aResult) {
970 return CallGetInterface(this, aResult);
971 }
972
973 NS_IMETHODIMP
GetDOMWindowID(uint64_t * aResult)974 nsDocLoader::GetDOMWindowID(uint64_t* aResult) {
975 *aResult = 0;
976
977 nsCOMPtr<mozIDOMWindowProxy> window;
978 nsresult rv = GetDOMWindow(getter_AddRefs(window));
979 NS_ENSURE_SUCCESS(rv, rv);
980
981 nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window);
982 NS_ENSURE_STATE(piwindow);
983
984 *aResult = piwindow->WindowID();
985 return NS_OK;
986 }
987
988 NS_IMETHODIMP
GetInnerDOMWindowID(uint64_t * aResult)989 nsDocLoader::GetInnerDOMWindowID(uint64_t* aResult) {
990 *aResult = 0;
991
992 nsCOMPtr<mozIDOMWindowProxy> window;
993 nsresult rv = GetDOMWindow(getter_AddRefs(window));
994 NS_ENSURE_SUCCESS(rv, rv);
995
996 nsCOMPtr<nsPIDOMWindowOuter> outer = nsPIDOMWindowOuter::From(window);
997 NS_ENSURE_STATE(outer);
998
999 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
1000 if (!inner) {
1001 // If we don't have an inner window, return 0.
1002 return NS_OK;
1003 }
1004
1005 *aResult = inner->WindowID();
1006 return NS_OK;
1007 }
1008
1009 NS_IMETHODIMP
GetIsTopLevel(bool * aResult)1010 nsDocLoader::GetIsTopLevel(bool* aResult) {
1011 *aResult = false;
1012
1013 nsCOMPtr<mozIDOMWindowProxy> window;
1014 GetDOMWindow(getter_AddRefs(window));
1015 if (window) {
1016 nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window);
1017 NS_ENSURE_STATE(piwindow);
1018
1019 nsCOMPtr<nsPIDOMWindowOuter> topWindow = piwindow->GetInProcessTop();
1020 *aResult = piwindow == topWindow;
1021 }
1022
1023 return NS_OK;
1024 }
1025
1026 NS_IMETHODIMP
GetIsLoadingDocument(bool * aIsLoadingDocument)1027 nsDocLoader::GetIsLoadingDocument(bool* aIsLoadingDocument) {
1028 *aIsLoadingDocument = mIsLoadingDocument;
1029
1030 return NS_OK;
1031 }
1032
1033 NS_IMETHODIMP
GetLoadType(uint32_t * aLoadType)1034 nsDocLoader::GetLoadType(uint32_t* aLoadType) {
1035 *aLoadType = 0;
1036
1037 return NS_ERROR_NOT_IMPLEMENTED;
1038 }
1039
1040 NS_IMETHODIMP
GetTarget(nsIEventTarget ** aTarget)1041 nsDocLoader::GetTarget(nsIEventTarget** aTarget) {
1042 nsCOMPtr<mozIDOMWindowProxy> window;
1043 nsresult rv = GetDOMWindow(getter_AddRefs(window));
1044 NS_ENSURE_SUCCESS(rv, rv);
1045
1046 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
1047 NS_ENSURE_STATE(global);
1048
1049 nsCOMPtr<nsIEventTarget> target =
1050 global->EventTargetFor(mozilla::TaskCategory::Other);
1051 target.forget(aTarget);
1052 return NS_OK;
1053 }
1054
1055 NS_IMETHODIMP
SetTarget(nsIEventTarget * aTarget)1056 nsDocLoader::SetTarget(nsIEventTarget* aTarget) {
1057 return NS_ERROR_NOT_IMPLEMENTED;
1058 }
1059
GetMaxTotalProgress()1060 int64_t nsDocLoader::GetMaxTotalProgress() {
1061 int64_t newMaxTotal = 0;
1062
1063 uint32_t count = mChildList.Length();
1064 for (uint32_t i = 0; i < count; i++) {
1065 int64_t individualProgress = 0;
1066 nsIDocumentLoader* docloader = ChildAt(i);
1067 if (docloader) {
1068 // Cast is safe since all children are nsDocLoader too
1069 individualProgress = ((nsDocLoader*)docloader)->GetMaxTotalProgress();
1070 }
1071 if (individualProgress < int64_t(0)) // if one of the elements doesn't know
1072 // it's size then none of them do
1073 {
1074 newMaxTotal = int64_t(-1);
1075 break;
1076 } else
1077 newMaxTotal += individualProgress;
1078 }
1079
1080 int64_t progress = -1;
1081 if (mMaxSelfProgress >= int64_t(0) && newMaxTotal >= int64_t(0))
1082 progress = newMaxTotal + mMaxSelfProgress;
1083
1084 return progress;
1085 }
1086
1087 ////////////////////////////////////////////////////////////////////////////////////
1088 // The following section contains support for nsIProgressEventSink which is used
1089 // to pass progress and status between the actual request and the doc loader.
1090 // The doc loader then turns around and makes the right web progress calls based
1091 // on this information.
1092 ////////////////////////////////////////////////////////////////////////////////////
1093
OnProgress(nsIRequest * aRequest,int64_t aProgress,int64_t aProgressMax)1094 NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest* aRequest, int64_t aProgress,
1095 int64_t aProgressMax) {
1096 int64_t progressDelta = 0;
1097
1098 //
1099 // Update the RequestInfo entry with the new progress data
1100 //
1101 if (nsRequestInfo* info = GetRequestInfo(aRequest)) {
1102 // Update info->mCurrentProgress before we call FireOnStateChange,
1103 // since that can make the "info" pointer invalid.
1104 int64_t oldCurrentProgress = info->mCurrentProgress;
1105 progressDelta = aProgress - oldCurrentProgress;
1106 info->mCurrentProgress = aProgress;
1107
1108 // suppress sending STATE_TRANSFERRING if this is upload progress (see bug
1109 // 240053)
1110 if (!info->mUploading && (int64_t(0) == oldCurrentProgress) &&
1111 (int64_t(0) == info->mMaxProgress)) {
1112 //
1113 // If we receive an OnProgress event from a toplevel channel that the URI
1114 // Loader has not yet targeted, then we must suppress the event. This is
1115 // necessary to ensure that webprogresslisteners do not get confused when
1116 // the channel is finally targeted. See bug 257308.
1117 //
1118 nsLoadFlags lf = 0;
1119 aRequest->GetLoadFlags(&lf);
1120 if ((lf & nsIChannel::LOAD_DOCUMENT_URI) &&
1121 !(lf & nsIChannel::LOAD_TARGETED)) {
1122 MOZ_LOG(
1123 gDocLoaderLog, LogLevel::Debug,
1124 ("DocLoader:%p Ignoring OnProgress while load is not targeted\n",
1125 this));
1126 return NS_OK;
1127 }
1128
1129 //
1130 // This is the first progress notification for the entry. If
1131 // (aMaxProgress != -1) then the content-length of the data is known,
1132 // so update mMaxSelfProgress... Otherwise, set it to -1 to indicate
1133 // that the content-length is no longer known.
1134 //
1135 if (aProgressMax != -1) {
1136 mMaxSelfProgress += aProgressMax;
1137 info->mMaxProgress = aProgressMax;
1138 } else {
1139 mMaxSelfProgress = int64_t(-1);
1140 info->mMaxProgress = int64_t(-1);
1141 }
1142
1143 // Send a STATE_TRANSFERRING notification for the request.
1144 int32_t flags;
1145
1146 flags = nsIWebProgressListener::STATE_TRANSFERRING |
1147 nsIWebProgressListener::STATE_IS_REQUEST;
1148 //
1149 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
1150 //
1151 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
1152 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
1153
1154 // Send STATE_TRANSFERRING for the document too...
1155 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1156 }
1157
1158 FireOnStateChange(this, aRequest, flags, NS_OK);
1159 }
1160
1161 // Update our overall current progress count.
1162 mCurrentSelfProgress += progressDelta;
1163 }
1164 //
1165 // The request is not part of the load group, so ignore its progress
1166 // information...
1167 //
1168 else {
1169 #if defined(DEBUG)
1170 nsAutoCString buffer;
1171
1172 GetURIStringFromRequest(aRequest, buffer);
1173 MOZ_LOG(
1174 gDocLoaderLog, LogLevel::Debug,
1175 ("DocLoader:%p OOPS - No Request Info for: %s\n", this, buffer.get()));
1176 #endif /* DEBUG */
1177
1178 return NS_OK;
1179 }
1180
1181 //
1182 // Fire progress notifications out to any registered nsIWebProgressListeners
1183 //
1184 FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
1185 mCurrentTotalProgress, mMaxTotalProgress);
1186
1187 return NS_OK;
1188 }
1189
OnStatus(nsIRequest * aRequest,nsresult aStatus,const char16_t * aStatusArg)1190 NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsresult aStatus,
1191 const char16_t* aStatusArg) {
1192 //
1193 // Fire progress notifications out to any registered nsIWebProgressListeners
1194 //
1195 if (aStatus != NS_OK) {
1196 // Remember the current status for this request
1197 nsRequestInfo* info;
1198 info = GetRequestInfo(aRequest);
1199 if (info) {
1200 bool uploading = (aStatus == NS_NET_STATUS_WRITING ||
1201 aStatus == NS_NET_STATUS_SENDING_TO);
1202 // If switching from uploading to downloading (or vice versa), then we
1203 // need to reset our progress counts. This is designed with HTTP form
1204 // submission in mind, where an upload is performed followed by download
1205 // of possibly several documents.
1206 if (info->mUploading != uploading) {
1207 mCurrentSelfProgress = mMaxSelfProgress = 0;
1208 mCurrentTotalProgress = mMaxTotalProgress = 0;
1209 mCompletedTotalProgress = 0;
1210 info->mUploading = uploading;
1211 info->mCurrentProgress = 0;
1212 info->mMaxProgress = 0;
1213 }
1214 }
1215
1216 nsCOMPtr<nsIStringBundleService> sbs =
1217 mozilla::services::GetStringBundleService();
1218 if (!sbs) return NS_ERROR_FAILURE;
1219 nsAutoString msg;
1220 nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg, msg);
1221 if (NS_FAILED(rv)) return rv;
1222
1223 // Keep around the message. In case a request finishes, we need to make sure
1224 // to send the status message of another request to our user to that we
1225 // don't display, for example, "Transferring" messages for requests that are
1226 // already done.
1227 if (info) {
1228 if (!info->mLastStatus) {
1229 info->mLastStatus = MakeUnique<nsStatusInfo>(aRequest);
1230 } else {
1231 // We're going to move it to the front of the list, so remove
1232 // it from wherever it is now.
1233 info->mLastStatus->remove();
1234 }
1235 info->mLastStatus->mStatusMessage = msg;
1236 info->mLastStatus->mStatusCode = aStatus;
1237 // Put the info at the front of the list
1238 mStatusInfoList.insertFront(info->mLastStatus.get());
1239 }
1240 FireOnStatusChange(this, aRequest, aStatus, msg.get());
1241 }
1242 return NS_OK;
1243 }
1244
ClearInternalProgress()1245 void nsDocLoader::ClearInternalProgress() {
1246 ClearRequestInfoHash();
1247
1248 mCurrentSelfProgress = mMaxSelfProgress = 0;
1249 mCurrentTotalProgress = mMaxTotalProgress = 0;
1250 mCompletedTotalProgress = 0;
1251
1252 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
1253 }
1254
1255 /**
1256 * |_code| is executed for every listener matching |_flag|
1257 * |listener| should be used inside |_code| as the nsIWebProgressListener var.
1258 */
1259 #define NOTIFY_LISTENERS(_flag, _code) \
1260 PR_BEGIN_MACRO \
1261 nsCOMPtr<nsIWebProgressListener> listener; \
1262 ListenerArray::BackwardIterator iter(mListenerInfoList); \
1263 while (iter.HasMore()) { \
1264 nsListenerInfo& info = iter.GetNext(); \
1265 if (!(info.mNotifyMask & (_flag))) { \
1266 continue; \
1267 } \
1268 listener = do_QueryReferent(info.mWeakListener); \
1269 if (!listener) { \
1270 iter.Remove(); \
1271 continue; \
1272 } \
1273 _code \
1274 } \
1275 mListenerInfoList.Compact(); \
1276 PR_END_MACRO
1277
FireOnProgressChange(nsDocLoader * aLoadInitiator,nsIRequest * request,int64_t aProgress,int64_t aProgressMax,int64_t aProgressDelta,int64_t aTotalProgress,int64_t aMaxTotalProgress)1278 void nsDocLoader::FireOnProgressChange(nsDocLoader* aLoadInitiator,
1279 nsIRequest* request, int64_t aProgress,
1280 int64_t aProgressMax,
1281 int64_t aProgressDelta,
1282 int64_t aTotalProgress,
1283 int64_t aMaxTotalProgress) {
1284 if (mIsLoadingDocument) {
1285 mCurrentTotalProgress += aProgressDelta;
1286 mMaxTotalProgress = GetMaxTotalProgress();
1287
1288 aTotalProgress = mCurrentTotalProgress;
1289 aMaxTotalProgress = mMaxTotalProgress;
1290 }
1291
1292 #if defined(DEBUG)
1293 nsAutoCString buffer;
1294
1295 GetURIStringFromRequest(request, buffer);
1296 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
1297 ("DocLoader:%p: Progress (%s): curSelf: %" PRId64 " maxSelf: %" PRId64
1298 " curTotal: %" PRId64 " maxTotal %" PRId64 "\n",
1299 this, buffer.get(), aProgress, aProgressMax, aTotalProgress,
1300 aMaxTotalProgress));
1301 #endif /* DEBUG */
1302
1303 NOTIFY_LISTENERS(
1304 nsIWebProgress::NOTIFY_PROGRESS,
1305 // XXX truncates 64-bit to 32-bit
1306 listener->OnProgressChange(aLoadInitiator, request, int32_t(aProgress),
1307 int32_t(aProgressMax), int32_t(aTotalProgress),
1308 int32_t(aMaxTotalProgress)););
1309
1310 // Pass the notification up to the parent...
1311 if (mParent) {
1312 mParent->FireOnProgressChange(aLoadInitiator, request, aProgress,
1313 aProgressMax, aProgressDelta, aTotalProgress,
1314 aMaxTotalProgress);
1315 }
1316 }
1317
GatherAncestorWebProgresses(WebProgressList & aList)1318 void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList) {
1319 for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
1320 aList.AppendElement(loader);
1321 }
1322 }
1323
FireOnStateChange(nsIWebProgress * aProgress,nsIRequest * aRequest,int32_t aStateFlags,nsresult aStatus)1324 void nsDocLoader::FireOnStateChange(nsIWebProgress* aProgress,
1325 nsIRequest* aRequest, int32_t aStateFlags,
1326 nsresult aStatus) {
1327 WebProgressList list;
1328 GatherAncestorWebProgresses(list);
1329 for (uint32_t i = 0; i < list.Length(); ++i) {
1330 list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
1331 }
1332 }
1333
DoFireOnStateChange(nsIWebProgress * const aProgress,nsIRequest * const aRequest,int32_t & aStateFlags,const nsresult aStatus)1334 void nsDocLoader::DoFireOnStateChange(nsIWebProgress* const aProgress,
1335 nsIRequest* const aRequest,
1336 int32_t& aStateFlags,
1337 const nsresult aStatus) {
1338 //
1339 // Remove the STATE_IS_NETWORK bit if necessary.
1340 //
1341 // The rule is to remove this bit, if the notification has been passed
1342 // up from a child WebProgress, and the current WebProgress is already
1343 // active...
1344 //
1345 if (mIsLoadingDocument &&
1346 (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) &&
1347 (this != aProgress)) {
1348 aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
1349 }
1350
1351 // Add the STATE_RESTORING bit if necessary.
1352 if (mIsRestoringDocument)
1353 aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
1354
1355 #if defined(DEBUG)
1356 nsAutoCString buffer;
1357
1358 GetURIStringFromRequest(aRequest, buffer);
1359 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
1360 ("DocLoader:%p: Status (%s): code: %x\n", this, buffer.get(),
1361 aStateFlags));
1362 #endif /* DEBUG */
1363
1364 NS_ASSERTION(aRequest,
1365 "Firing OnStateChange(...) notification with a NULL request!");
1366
1367 NOTIFY_LISTENERS(
1368 ((aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL),
1369 listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus););
1370 }
1371
FireOnLocationChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsIURI * aUri,uint32_t aFlags)1372 void nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
1373 nsIRequest* aRequest, nsIURI* aUri,
1374 uint32_t aFlags) {
1375 NOTIFY_LISTENERS(
1376 nsIWebProgress::NOTIFY_LOCATION,
1377 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
1378 ("DocLoader [%p] calling %p->OnLocationChange to %s %x", this,
1379 listener.get(), aUri->GetSpecOrDefault().get(), aFlags));
1380 listener->OnLocationChange(aWebProgress, aRequest, aUri, aFlags););
1381
1382 // Pass the notification up to the parent...
1383 if (mParent) {
1384 mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
1385 }
1386 }
1387
FireOnStatusChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsresult aStatus,const char16_t * aMessage)1388 void nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
1389 nsIRequest* aRequest, nsresult aStatus,
1390 const char16_t* aMessage) {
1391 NOTIFY_LISTENERS(
1392 nsIWebProgress::NOTIFY_STATUS,
1393 listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage););
1394
1395 // Pass the notification up to the parent...
1396 if (mParent) {
1397 mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
1398 }
1399 }
1400
RefreshAttempted(nsIWebProgress * aWebProgress,nsIURI * aURI,int32_t aDelay,bool aSameURI)1401 bool nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress, nsIURI* aURI,
1402 int32_t aDelay, bool aSameURI) {
1403 /*
1404 * Returns true if the refresh may proceed,
1405 * false if the refresh should be blocked.
1406 */
1407 bool allowRefresh = true;
1408
1409 NOTIFY_LISTENERS(
1410 nsIWebProgress::NOTIFY_REFRESH,
1411 nsCOMPtr<nsIWebProgressListener2> listener2 =
1412 do_QueryReferent(info.mWeakListener);
1413 if (!listener2) continue;
1414
1415 bool listenerAllowedRefresh;
1416 nsresult listenerRV = listener2->OnRefreshAttempted(
1417 aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
1418 if (NS_FAILED(listenerRV)) continue;
1419
1420 allowRefresh = allowRefresh && listenerAllowedRefresh;);
1421
1422 // Pass the notification up to the parent...
1423 if (mParent) {
1424 allowRefresh = allowRefresh && mParent->RefreshAttempted(aWebProgress, aURI,
1425 aDelay, aSameURI);
1426 }
1427
1428 return allowRefresh;
1429 }
1430
AddRequestInfo(nsIRequest * aRequest)1431 nsresult nsDocLoader::AddRequestInfo(nsIRequest* aRequest) {
1432 if (!mRequestInfoHash.Add(aRequest, mozilla::fallible)) {
1433 return NS_ERROR_OUT_OF_MEMORY;
1434 }
1435
1436 return NS_OK;
1437 }
1438
RemoveRequestInfo(nsIRequest * aRequest)1439 void nsDocLoader::RemoveRequestInfo(nsIRequest* aRequest) {
1440 mRequestInfoHash.Remove(aRequest);
1441 }
1442
GetRequestInfo(nsIRequest * aRequest) const1443 nsDocLoader::nsRequestInfo* nsDocLoader::GetRequestInfo(
1444 nsIRequest* aRequest) const {
1445 return static_cast<nsRequestInfo*>(mRequestInfoHash.Search(aRequest));
1446 }
1447
ClearRequestInfoHash(void)1448 void nsDocLoader::ClearRequestInfoHash(void) { mRequestInfoHash.Clear(); }
1449
CalculateMaxProgress()1450 int64_t nsDocLoader::CalculateMaxProgress() {
1451 int64_t max = mCompletedTotalProgress;
1452 for (auto iter = mRequestInfoHash.Iter(); !iter.Done(); iter.Next()) {
1453 auto info = static_cast<const nsRequestInfo*>(iter.Get());
1454
1455 if (info->mMaxProgress < info->mCurrentProgress) {
1456 return int64_t(-1);
1457 }
1458 max += info->mMaxProgress;
1459 }
1460 return max;
1461 }
1462
AsyncOnChannelRedirect(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aFlags,nsIAsyncVerifyRedirectCallback * cb)1463 NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(
1464 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
1465 nsIAsyncVerifyRedirectCallback* cb) {
1466 if (aOldChannel) {
1467 nsLoadFlags loadFlags = 0;
1468 int32_t stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
1469 nsIWebProgressListener::STATE_IS_REQUEST;
1470
1471 aOldChannel->GetLoadFlags(&loadFlags);
1472 // If the document channel is being redirected, then indicate that the
1473 // document is being redirected in the notification...
1474 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
1475 stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
1476
1477 #if defined(DEBUG)
1478 // We only set mDocumentRequest in OnStartRequest(), but its possible
1479 // to get a redirect before that for service worker interception.
1480 if (mDocumentRequest) {
1481 nsCOMPtr<nsIRequest> request(aOldChannel);
1482 NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
1483 }
1484 #endif /* DEBUG */
1485 }
1486
1487 OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
1488 FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
1489 }
1490
1491 cb->OnRedirectVerifyCallback(NS_OK);
1492 return NS_OK;
1493 }
1494
OnSecurityChange(nsISupports * aContext,uint32_t aState)1495 void nsDocLoader::OnSecurityChange(nsISupports* aContext, uint32_t aState) {
1496 //
1497 // Fire progress notifications out to any registered nsIWebProgressListeners.
1498 //
1499
1500 nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
1501 nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
1502
1503 NOTIFY_LISTENERS(nsIWebProgress::NOTIFY_SECURITY,
1504 listener->OnSecurityChange(webProgress, request, aState););
1505
1506 // Pass the notification up to the parent...
1507 if (mParent) {
1508 mParent->OnSecurityChange(aContext, aState);
1509 }
1510 }
1511
1512 /*
1513 * Implementation of nsISupportsPriority methods...
1514 *
1515 * The priority of the DocLoader _is_ the priority of its LoadGroup.
1516 *
1517 * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
1518 * go away.
1519 */
1520
GetPriority(int32_t * aPriority)1521 NS_IMETHODIMP nsDocLoader::GetPriority(int32_t* aPriority) {
1522 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1523 if (p) return p->GetPriority(aPriority);
1524
1525 *aPriority = 0;
1526 return NS_OK;
1527 }
1528
SetPriority(int32_t aPriority)1529 NS_IMETHODIMP nsDocLoader::SetPriority(int32_t aPriority) {
1530 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
1531 ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
1532
1533 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1534 if (p) p->SetPriority(aPriority);
1535
1536 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader, SetPriority,
1537 (aPriority));
1538
1539 return NS_OK;
1540 }
1541
AdjustPriority(int32_t aDelta)1542 NS_IMETHODIMP nsDocLoader::AdjustPriority(int32_t aDelta) {
1543 MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
1544 ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
1545
1546 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
1547 if (p) p->AdjustPriority(aDelta);
1548
1549 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
1550 AdjustPriority, (aDelta));
1551
1552 return NS_OK;
1553 }
1554
1555 #if 0
1556 void nsDocLoader::DumpChannelInfo()
1557 {
1558 nsChannelInfo *info;
1559 int32_t i, count;
1560 int32_t current=0, max=0;
1561
1562
1563 printf("==== DocLoader=%x\n", this);
1564
1565 count = mChannelInfoList.Count();
1566 for(i=0; i<count; i++) {
1567 info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
1568
1569 # if defined(DEBUG)
1570 nsAutoCString buffer;
1571 nsresult rv = NS_OK;
1572 if (info->mURI) {
1573 rv = info->mURI->GetSpec(buffer);
1574 }
1575
1576 printf(" [%d] current=%d max=%d [%s]\n", i,
1577 info->mCurrentProgress,
1578 info->mMaxProgress, buffer.get());
1579 # endif /* DEBUG */
1580
1581 current += info->mCurrentProgress;
1582 if (max >= 0) {
1583 if (info->mMaxProgress < info->mCurrentProgress) {
1584 max = -1;
1585 } else {
1586 max += info->mMaxProgress;
1587 }
1588 }
1589 }
1590
1591 printf("\nCurrent=%d Total=%d\n====\n", current, max);
1592 }
1593 #endif /* 0 */
1594