Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/RequestContextService.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ;*; */
2
/* vim: set sw=2 ts=8 et tw=80 : */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsAutoPtr.h"
8
#include "nsIDocShell.h"
9
#include "nsIDocument.h"
10
#include "nsIDocumentLoader.h"
11
#include "nsIObserverService.h"
12
#include "nsIXULRuntime.h"
13
#include "nsServiceManagerUtils.h"
14
#include "nsThreadUtils.h"
15
#include "RequestContextService.h"
16
17
#include "mozilla/Atomics.h"
18
#include "mozilla/Logging.h"
19
#include "mozilla/Services.h"
20
#include "mozilla/TimeStamp.h"
21
22
#include "mozilla/net/NeckoChild.h"
23
#include "mozilla/net/NeckoCommon.h"
24
#include "mozilla/net/PSpdyPush.h"
25
26
#include "../protocol/http/nsHttpHandler.h"
27
28
namespace mozilla {
29
namespace net {
30
31
LazyLogModule gRequestContextLog("RequestContext");
32
#undef LOG
33
3
#define LOG(args) MOZ_LOG(gRequestContextLog, LogLevel::Info, args)
34
35
// This is used to prevent adding tail pending requests after shutdown
36
static bool sShutdown = false;
37
38
// nsIRequestContext
39
class RequestContext final : public nsIRequestContext
40
                           , public nsITimerCallback
41
{
42
public:
43
  NS_DECL_THREADSAFE_ISUPPORTS
44
  NS_DECL_NSIREQUESTCONTEXT
45
  NS_DECL_NSITIMERCALLBACK
46
47
  explicit RequestContext(const uint64_t id);
48
private:
49
  virtual ~RequestContext();
50
51
  void ProcessTailQueue(nsresult aResult);
52
  // Reschedules the timer if needed
53
  void ScheduleUnblock();
54
  // Hard-reschedules the timer
55
  void RescheduleUntailTimer(TimeStamp const& now);
56
57
  uint64_t mID;
58
  Atomic<uint32_t>       mBlockingTransactionCount;
59
  nsAutoPtr<SpdyPushCache> mSpdyCache;
60
  nsCString mUserAgentOverride;
61
62
  typedef nsCOMPtr<nsIRequestTailUnblockCallback> PendingTailRequest;
63
  // Number of known opened non-tailed requets
64
  uint32_t mNonTailRequests;
65
  // Queue of requests that have been tailed, when conditions are met
66
  // we call each of them to unblock and drop the reference
67
  nsTArray<PendingTailRequest> mTailQueue;
68
  // Loosly scheduled timer, never scheduled further to the future than
69
  // mUntailAt time
70
  nsCOMPtr<nsITimer> mUntailTimer;
71
  // Timestamp when the timer is expected to fire,
72
  // always less than or equal to mUntailAt
73
  TimeStamp mTimerScheduledAt;
74
  // Timestamp when we want to actually untail queued requets based on
75
  // the number of request count change in the past; iff this timestamp
76
  // is set, we tail requests
77
  TimeStamp mUntailAt;
78
79
  // Timestamp of the navigation start time, set to Now() in BeginLoad().
80
  // This is used to progressively lower the maximum delay time so that
81
  // we can't get to a situation when a number of repetitive requests
82
  // on the page causes forever tailing.
83
  TimeStamp mBeginLoadTime;
84
85
  // This member is true only between DOMContentLoaded notification and
86
  // next document load beginning for this request context.
87
  // Top level request contexts are recycled.
88
  bool mAfterDOMContentLoaded;
89
};
90
91
NS_IMPL_ISUPPORTS(RequestContext, nsIRequestContext, nsITimerCallback)
92
93
RequestContext::RequestContext(const uint64_t aID)
94
  : mID(aID)
95
  , mBlockingTransactionCount(0)
96
  , mNonTailRequests(0)
97
  , mAfterDOMContentLoaded(false)
98
3
{
99
3
  LOG(("RequestContext::RequestContext this=%p id=%" PRIx64, this, mID));
100
3
}
101
102
RequestContext::~RequestContext()
103
0
{
104
0
  MOZ_ASSERT(mTailQueue.Length() == 0);
105
0
106
0
  LOG(("RequestContext::~RequestContext this=%p blockers=%u",
107
0
       this, static_cast<uint32_t>(mBlockingTransactionCount)));
108
0
}
109
110
NS_IMETHODIMP
111
RequestContext::BeginLoad()
112
0
{
113
0
  MOZ_ASSERT(NS_IsMainThread());
114
0
115
0
  LOG(("RequestContext::BeginLoad %p", this));
116
0
117
0
  if (IsNeckoChild()) {
118
0
    // Tailing is not supported on the child process
119
0
    if (gNeckoChild) {
120
0
      gNeckoChild->SendRequestContextLoadBegin(mID);
121
0
    }
122
0
    return NS_OK;
123
0
  }
124
0
125
0
  mAfterDOMContentLoaded = false;
126
0
  mBeginLoadTime = TimeStamp::NowLoRes();
127
0
  return NS_OK;
128
0
}
129
130
NS_IMETHODIMP
131
RequestContext::DOMContentLoaded()
132
0
{
133
0
  MOZ_ASSERT(NS_IsMainThread());
134
0
135
0
  LOG(("RequestContext::DOMContentLoaded %p", this));
136
0
137
0
  if (IsNeckoChild()) {
138
0
    // Tailing is not supported on the child process
139
0
    if (gNeckoChild) {
140
0
      gNeckoChild->SendRequestContextAfterDOMContentLoaded(mID);
141
0
    }
142
0
    return NS_OK;
143
0
  }
144
0
145
0
  if (mAfterDOMContentLoaded) {
146
0
    // There is a possibility of a duplicate notification
147
0
    return NS_OK;
148
0
  }
149
0
150
0
  mAfterDOMContentLoaded = true;
151
0
152
0
  // Conditions for the delay calculation has changed.
153
0
  ScheduleUnblock();
154
0
  return NS_OK;
155
0
}
156
157
NS_IMETHODIMP
158
RequestContext::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount)
159
0
{
160
0
  NS_ENSURE_ARG_POINTER(aBlockingTransactionCount);
161
0
  *aBlockingTransactionCount = mBlockingTransactionCount;
162
0
  return NS_OK;
163
0
}
164
165
NS_IMETHODIMP
166
RequestContext::AddBlockingTransaction()
167
0
{
168
0
  mBlockingTransactionCount++;
169
0
  LOG(("RequestContext::AddBlockingTransaction this=%p blockers=%u",
170
0
       this, static_cast<uint32_t>(mBlockingTransactionCount)));
171
0
  return NS_OK;
172
0
}
173
174
NS_IMETHODIMP
175
RequestContext::RemoveBlockingTransaction(uint32_t *outval)
176
0
{
177
0
  NS_ENSURE_ARG_POINTER(outval);
178
0
  mBlockingTransactionCount--;
179
0
  LOG(("RequestContext::RemoveBlockingTransaction this=%p blockers=%u",
180
0
       this, static_cast<uint32_t>(mBlockingTransactionCount)));
181
0
  *outval = mBlockingTransactionCount;
182
0
  return NS_OK;
183
0
}
184
185
NS_IMETHODIMP
186
RequestContext::GetSpdyPushCache(mozilla::net::SpdyPushCache **aSpdyPushCache)
187
0
{
188
0
  *aSpdyPushCache = mSpdyCache.get();
189
0
  return NS_OK;
190
0
}
191
192
NS_IMETHODIMP
193
RequestContext::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache)
194
0
{
195
0
  mSpdyCache = aSpdyPushCache;
196
0
  return NS_OK;
197
0
}
198
199
NS_IMETHODIMP
200
RequestContext::GetID(uint64_t *outval)
201
0
{
202
0
  NS_ENSURE_ARG_POINTER(outval);
203
0
  *outval = mID;
204
0
  return NS_OK;
205
0
}
206
207
NS_IMETHODIMP
208
RequestContext::GetUserAgentOverride(nsACString& aUserAgentOverride)
209
0
{
210
0
  aUserAgentOverride = mUserAgentOverride;
211
0
  return NS_OK;
212
0
}
213
214
NS_IMETHODIMP
215
RequestContext::SetUserAgentOverride(const nsACString& aUserAgentOverride)
216
0
{
217
0
  mUserAgentOverride = aUserAgentOverride;
218
0
  return NS_OK;
219
0
}
220
221
NS_IMETHODIMP
222
RequestContext::AddNonTailRequest()
223
0
{
224
0
  MOZ_ASSERT(NS_IsMainThread());
225
0
226
0
  ++mNonTailRequests;
227
0
  LOG(("RequestContext::AddNonTailRequest this=%p, cnt=%u",
228
0
       this, mNonTailRequests));
229
0
230
0
  ScheduleUnblock();
231
0
  return NS_OK;
232
0
}
233
234
NS_IMETHODIMP
235
RequestContext::RemoveNonTailRequest()
236
0
{
237
0
  MOZ_ASSERT(NS_IsMainThread());
238
0
  MOZ_ASSERT(mNonTailRequests > 0);
239
0
240
0
  LOG(("RequestContext::RemoveNonTailRequest this=%p, cnt=%u",
241
0
       this, mNonTailRequests - 1));
242
0
243
0
  --mNonTailRequests;
244
0
245
0
  ScheduleUnblock();
246
0
  return NS_OK;
247
0
}
248
249
void
250
RequestContext::ScheduleUnblock()
251
0
{
252
0
  MOZ_ASSERT(!IsNeckoChild());
253
0
  MOZ_ASSERT(NS_IsMainThread());
254
0
255
0
  if (!gHttpHandler) {
256
0
    return;
257
0
  }
258
0
259
0
  uint32_t quantum = gHttpHandler->TailBlockingDelayQuantum(mAfterDOMContentLoaded);
260
0
  uint32_t delayMax = gHttpHandler->TailBlockingDelayMax();
261
0
  uint32_t totalMax = gHttpHandler->TailBlockingTotalMax();
262
0
263
0
  if (!mBeginLoadTime.IsNull()) {
264
0
    // We decrease the maximum delay progressively with the time since the page load
265
0
    // begin.  This seems like a reasonable and clear heuristic allowing us to start
266
0
    // loading tailed requests in a deterministic time after the load has started.
267
0
268
0
    uint32_t sinceBeginLoad = static_cast<uint32_t>(
269
0
      (TimeStamp::NowLoRes() - mBeginLoadTime).ToMilliseconds());
270
0
    uint32_t tillTotal = totalMax - std::min(sinceBeginLoad, totalMax);
271
0
    uint32_t proportion = totalMax // values clamped between 0 and 60'000
272
0
      ? (delayMax * tillTotal) / totalMax
273
0
      : 0;
274
0
    delayMax = std::min(delayMax, proportion);
275
0
  }
276
0
277
0
  CheckedInt<uint32_t> delay = quantum * mNonTailRequests;
278
0
279
0
  if (!mAfterDOMContentLoaded) {
280
0
    // Before DOMContentLoaded notification we want to make sure that tailed
281
0
    // requests don't start when there is a short delay during which we may
282
0
    // not have any active requests on the page happening.
283
0
    delay += quantum;
284
0
  }
285
0
286
0
  if (!delay.isValid() || delay.value() > delayMax) {
287
0
    delay = delayMax;
288
0
  }
289
0
290
0
  LOG(("RequestContext::ScheduleUnblock this=%p non-tails=%u tail-queue=%zu delay=%u after-DCL=%d",
291
0
       this, mNonTailRequests, mTailQueue.Length(), delay.value(), mAfterDOMContentLoaded));
292
0
293
0
  TimeStamp now = TimeStamp::NowLoRes();
294
0
  mUntailAt = now + TimeDuration::FromMilliseconds(delay.value());
295
0
296
0
  if (mTimerScheduledAt.IsNull() || mUntailAt < mTimerScheduledAt) {
297
0
    LOG(("RequestContext %p timer would fire too late, rescheduling", this));
298
0
    RescheduleUntailTimer(now);
299
0
  }
300
0
}
301
302
void
303
RequestContext::RescheduleUntailTimer(TimeStamp const& now)
304
0
{
305
0
  MOZ_ASSERT(mUntailAt >= now);
306
0
307
0
  if (mUntailTimer) {
308
0
    mUntailTimer->Cancel();
309
0
  }
310
0
311
0
  if (!mTailQueue.Length()) {
312
0
    mUntailTimer = nullptr;
313
0
    mTimerScheduledAt = TimeStamp();
314
0
    return;
315
0
  }
316
0
317
0
  TimeDuration interval = mUntailAt - now;
318
0
  if (!mTimerScheduledAt.IsNull() && mUntailAt < mTimerScheduledAt) {
319
0
    // When the number of untailed requests goes down,
320
0
    // let's half the interval, since it's likely we would
321
0
    // reschedule for a shorter time again very soon.
322
0
    // This will likely save rescheduling this timer.
323
0
    interval = interval / int64_t(2);
324
0
    mTimerScheduledAt = mUntailAt - interval;
325
0
  } else {
326
0
    mTimerScheduledAt = mUntailAt;
327
0
  }
328
0
329
0
  uint32_t delay = interval.ToMilliseconds();
330
0
  mUntailTimer = do_CreateInstance("@mozilla.org/timer;1");
331
0
  mUntailTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
332
0
333
0
  LOG(("RequestContext::RescheduleUntailTimer %p in %d", this, delay));
334
0
}
335
336
NS_IMETHODIMP
337
RequestContext::Notify(nsITimer *timer)
338
0
{
339
0
  MOZ_ASSERT(NS_IsMainThread());
340
0
  MOZ_ASSERT(timer == mUntailTimer);
341
0
  MOZ_ASSERT(!mTimerScheduledAt.IsNull());
342
0
  MOZ_ASSERT(mTailQueue.Length());
343
0
344
0
  mUntailTimer = nullptr;
345
0
346
0
  TimeStamp now = TimeStamp::NowLoRes();
347
0
  if (mUntailAt > mTimerScheduledAt && mUntailAt > now) {
348
0
    LOG(("RequestContext %p timer fired too soon, rescheduling", this));
349
0
    RescheduleUntailTimer(now);
350
0
    return NS_OK;
351
0
  }
352
0
353
0
  // Must drop to allow re-engage of the timer
354
0
  mTimerScheduledAt = TimeStamp();
355
0
356
0
  ProcessTailQueue(NS_OK);
357
0
358
0
  return NS_OK;
359
0
}
360
361
NS_IMETHODIMP
362
RequestContext::IsContextTailBlocked(nsIRequestTailUnblockCallback * aRequest,
363
                                     bool *aBlocked)
364
0
{
365
0
  MOZ_ASSERT(NS_IsMainThread());
366
0
367
0
  LOG(("RequestContext::IsContextTailBlocked this=%p, request=%p, queued=%zu",
368
0
       this, aRequest, mTailQueue.Length()));
369
0
370
0
  *aBlocked = false;
371
0
372
0
  if (sShutdown) {
373
0
    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
374
0
  }
375
0
376
0
  if (mUntailAt.IsNull()) {
377
0
    LOG(("  untail time passed"));
378
0
    return NS_OK;
379
0
  }
380
0
381
0
  if (mAfterDOMContentLoaded && !mNonTailRequests) {
382
0
    LOG(("  after DOMContentLoaded and no untailed requests"));
383
0
    return NS_OK;
384
0
  }
385
0
386
0
  if (!gHttpHandler) {
387
0
    // Xpcshell tests may not have http handler
388
0
    LOG(("  missing gHttpHandler?"));
389
0
    return NS_OK;
390
0
  }
391
0
392
0
  *aBlocked = true;
393
0
  mTailQueue.AppendElement(aRequest);
394
0
395
0
  LOG(("  request queued"));
396
0
397
0
  if (!mUntailTimer) {
398
0
    ScheduleUnblock();
399
0
  }
400
0
401
0
  return NS_OK;
402
0
}
403
404
NS_IMETHODIMP
405
RequestContext::CancelTailedRequest(nsIRequestTailUnblockCallback * aRequest)
406
0
{
407
0
  MOZ_ASSERT(NS_IsMainThread());
408
0
409
0
  bool removed = mTailQueue.RemoveElement(aRequest);
410
0
411
0
  LOG(("RequestContext::CancelTailedRequest %p req=%p removed=%d",
412
0
       this, aRequest, removed));
413
0
414
0
  // Stop untail timer if all tail requests are canceled.
415
0
  if (removed && mTailQueue.IsEmpty()) {
416
0
    if (mUntailTimer) {
417
0
      mUntailTimer->Cancel();
418
0
      mUntailTimer = nullptr;
419
0
    }
420
0
421
0
    // Must drop to allow re-engage of the timer
422
0
    mTimerScheduledAt = TimeStamp();
423
0
  }
424
0
425
0
  return NS_OK;
426
0
}
427
428
void
429
RequestContext::ProcessTailQueue(nsresult aResult)
430
0
{
431
0
  LOG(("RequestContext::ProcessTailQueue this=%p, queued=%zu, rv=%" PRIx32,
432
0
       this, mTailQueue.Length(), static_cast<uint32_t>(aResult)));
433
0
434
0
  if (mUntailTimer) {
435
0
    mUntailTimer->Cancel();
436
0
    mUntailTimer = nullptr;
437
0
  }
438
0
439
0
  // Must drop to stop tailing requests
440
0
  mUntailAt = TimeStamp();
441
0
442
0
  nsTArray<PendingTailRequest> queue;
443
0
  queue.SwapElements(mTailQueue);
444
0
445
0
  for (const auto& request : queue) {
446
0
    LOG(("  untailing %p", request.get()));
447
0
    request->OnTailUnblock(aResult);
448
0
  }
449
0
}
450
451
NS_IMETHODIMP
452
RequestContext::CancelTailPendingRequests(nsresult aResult)
453
0
{
454
0
  MOZ_ASSERT(NS_IsMainThread());
455
0
  MOZ_ASSERT(NS_FAILED(aResult));
456
0
457
0
  ProcessTailQueue(aResult);
458
0
  return NS_OK;
459
0
}
460
461
//nsIRequestContextService
462
RequestContextService *RequestContextService::sSelf = nullptr;
463
464
NS_IMPL_ISUPPORTS(RequestContextService, nsIRequestContextService, nsIObserver)
465
466
RequestContextService::RequestContextService()
467
  : mRCIDNamespace(0)
468
  , mNextRCID(1)
469
3
{
470
3
  MOZ_ASSERT(!sSelf, "multiple rcs instances!");
471
3
  MOZ_ASSERT(NS_IsMainThread());
472
3
  sSelf = this;
473
3
474
3
  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
475
3
  runtime->GetProcessID(&mRCIDNamespace);
476
3
}
477
478
RequestContextService::~RequestContextService()
479
0
{
480
0
  MOZ_ASSERT(NS_IsMainThread());
481
0
  Shutdown();
482
0
  sSelf = nullptr;
483
0
}
484
485
nsresult
486
RequestContextService::Init()
487
3
{
488
3
  nsresult rv;
489
3
490
3
  MOZ_ASSERT(NS_IsMainThread());
491
3
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
492
3
  if (!obs) {
493
0
    return NS_ERROR_NOT_AVAILABLE;
494
0
  }
495
3
496
3
  rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
497
3
  if (NS_FAILED(rv)) {
498
0
    return rv;
499
0
  }
500
3
  obs->AddObserver(this, "content-document-interactive", false);
501
3
  if (NS_FAILED(rv)) {
502
0
    return rv;
503
0
  }
504
3
505
3
  return NS_OK;
506
3
}
507
508
void
509
RequestContextService::Shutdown()
510
0
{
511
0
  MOZ_ASSERT(NS_IsMainThread());
512
0
  // We need to do this to prevent the requests from being scheduled after
513
0
  // shutdown.
514
0
  for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
515
0
    iter.Data()->CancelTailPendingRequests(NS_ERROR_ABORT);
516
0
  }
517
0
  mTable.Clear();
518
0
  sShutdown = true;
519
0
}
520
521
/* static */ nsresult
522
RequestContextService::Create(nsISupports *aOuter, const nsIID& aIID, void **aResult)
523
0
{
524
0
  MOZ_ASSERT(NS_IsMainThread());
525
0
  if (aOuter != nullptr) {
526
0
    return NS_ERROR_NO_AGGREGATION;
527
0
  }
528
0
529
0
  RefPtr<RequestContextService> svc = new RequestContextService();
530
0
  nsresult rv = svc->Init();
531
0
  NS_ENSURE_SUCCESS(rv, rv);
532
0
533
0
  return svc->QueryInterface(aIID, aResult);
534
0
}
535
536
NS_IMETHODIMP
537
RequestContextService::GetRequestContext(const uint64_t rcID, nsIRequestContext **rc)
538
0
{
539
0
  MOZ_ASSERT(NS_IsMainThread());
540
0
  NS_ENSURE_ARG_POINTER(rc);
541
0
  *rc = nullptr;
542
0
543
0
  if (sShutdown) {
544
0
    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
545
0
  }
546
0
547
0
  if (!mTable.Get(rcID, rc)) {
548
0
    nsCOMPtr<nsIRequestContext> newSC = new RequestContext(rcID);
549
0
    mTable.Put(rcID, newSC);
550
0
    newSC.swap(*rc);
551
0
  }
552
0
553
0
  return NS_OK;
554
0
}
555
556
NS_IMETHODIMP
557
RequestContextService::GetRequestContextFromLoadGroup(nsILoadGroup *aLoadGroup, nsIRequestContext **rc)
558
0
{
559
0
  nsresult rv;
560
0
561
0
  uint64_t rcID;
562
0
  rv = aLoadGroup->GetRequestContextID(&rcID);
563
0
  if (NS_FAILED(rv)) {
564
0
    return rv;
565
0
  }
566
0
567
0
  return GetRequestContext(rcID, rc);
568
0
}
569
570
NS_IMETHODIMP
571
RequestContextService::NewRequestContext(nsIRequestContext **rc)
572
3
{
573
3
  MOZ_ASSERT(NS_IsMainThread());
574
3
  NS_ENSURE_ARG_POINTER(rc);
575
3
  *rc = nullptr;
576
3
577
3
  if (sShutdown) {
578
0
    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
579
0
  }
580
3
581
3
  uint64_t rcID = ((static_cast<uint64_t>(mRCIDNamespace) << 32) & 0xFFFFFFFF00000000LL) | mNextRCID++;
582
3
583
3
  nsCOMPtr<nsIRequestContext> newSC = new RequestContext(rcID);
584
3
  mTable.Put(rcID, newSC);
585
3
  newSC.swap(*rc);
586
3
587
3
  return NS_OK;
588
3
}
589
590
NS_IMETHODIMP
591
RequestContextService::RemoveRequestContext(const uint64_t rcID)
592
0
{
593
0
  if (IsNeckoChild() && gNeckoChild) {
594
0
    gNeckoChild->SendRemoveRequestContext(rcID);
595
0
  }
596
0
597
0
  MOZ_ASSERT(NS_IsMainThread());
598
0
  mTable.Remove(rcID);
599
0
  return NS_OK;
600
0
}
601
602
NS_IMETHODIMP
603
RequestContextService::Observe(nsISupports *subject, const char *topic,
604
                                  const char16_t *data_unicode)
605
0
{
606
0
  MOZ_ASSERT(NS_IsMainThread());
607
0
  if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
608
0
    Shutdown();
609
0
    return NS_OK;
610
0
  }
611
0
612
0
  if (!strcmp("content-document-interactive", topic)) {
613
0
    nsCOMPtr<nsIDocument> document(do_QueryInterface(subject));
614
0
    MOZ_ASSERT(document);
615
0
    // We want this be triggered also for iframes, since those track their
616
0
    // own request context ids.
617
0
    if (!document) {
618
0
      return NS_OK;
619
0
    }
620
0
    nsIDocShell* ds = document->GetDocShell();
621
0
    // XML documents don't always have a docshell assigned
622
0
    if (!ds) {
623
0
      return NS_OK;
624
0
    }
625
0
    nsCOMPtr<nsIDocumentLoader> dl(do_QueryInterface(ds));
626
0
    if (!dl) {
627
0
      return NS_OK;
628
0
    }
629
0
    nsCOMPtr<nsILoadGroup> lg;
630
0
    dl->GetLoadGroup(getter_AddRefs(lg));
631
0
    if (!lg) {
632
0
      return NS_OK;
633
0
    }
634
0
    nsCOMPtr<nsIRequestContext> rc;
635
0
    GetRequestContextFromLoadGroup(lg, getter_AddRefs(rc));
636
0
    if (rc) {
637
0
      rc->DOMContentLoaded();
638
0
    }
639
0
640
0
    return NS_OK;
641
0
  }
642
0
643
0
  MOZ_ASSERT(false, "Unexpected observer topic");
644
0
  return NS_OK;
645
0
}
646
647
} // ::mozilla::net
648
} // ::mozilla
649
650
#undef LOG