Coverage Report

Created: 2018-09-25 14:53

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