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