Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set sw=2 ts=4 sts=2 et cin: */
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 "mozilla/DebugOnly.h"
8
9
#include "nsLoadGroup.h"
10
11
#include "nsArrayEnumerator.h"
12
#include "nsCOMArray.h"
13
#include "nsCOMPtr.h"
14
#include "mozilla/Logging.h"
15
#include "nsString.h"
16
#include "nsTArray.h"
17
#include "mozilla/Telemetry.h"
18
#include "nsITimedChannel.h"
19
#include "nsIInterfaceRequestor.h"
20
#include "nsIRequestObserver.h"
21
#include "nsIRequestContext.h"
22
#include "CacheObserver.h"
23
#include "MainThreadUtils.h"
24
#include "RequestContextService.h"
25
#include "mozilla/Unused.h"
26
27
namespace mozilla {
28
namespace net {
29
30
//
31
// Log module for nsILoadGroup logging...
32
//
33
// To enable logging (see prlog.h for full details):
34
//
35
// set MOZ_LOG=LoadGroup:5
36
// set MOZ_LOG_FILE=network.log
37
//
38
// This enables LogLevel::Debug level information and places all output in
39
// the file network.log.
40
//
41
static LazyLogModule gLoadGroupLog("LoadGroup");
42
#undef LOG
43
#define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
44
45
////////////////////////////////////////////////////////////////////////////////
46
47
class RequestMapEntry : public PLDHashEntryHdr {
48
public:
49
explicit RequestMapEntry(nsIRequest* aRequest) : mKey(aRequest) {}
50
51
nsCOMPtr<nsIRequest> mKey;
52
};
53
54
static bool RequestHashMatchEntry(const PLDHashEntryHdr* entry,
55
const void* key) {
56
const RequestMapEntry* e = static_cast<const RequestMapEntry*>(entry);
57
const nsIRequest* request = static_cast<const nsIRequest*>(key);
58
59
return e->mKey == request;
60
}
61
62
static void RequestHashClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry) {
63
RequestMapEntry* e = static_cast<RequestMapEntry*>(entry);
64
65
// An entry is being cleared, let the entry do its own cleanup.
66
e->~RequestMapEntry();
67
}
68
69
static void RequestHashInitEntry(PLDHashEntryHdr* entry, const void* key) {
70
const nsIRequest* const_request = static_cast<const nsIRequest*>(key);
71
nsIRequest* request = const_cast<nsIRequest*>(const_request);
72
73
// Initialize the entry with placement new
74
new (entry) RequestMapEntry(request);
75
}
76
77
static const PLDHashTableOps sRequestHashOps = {
78
PLDHashTable::HashVoidPtrKeyStub, RequestHashMatchEntry,
79
PLDHashTable::MoveEntryStub, RequestHashClearEntry, RequestHashInitEntry};
80
81
static void RescheduleRequest(nsIRequest* aRequest, int32_t delta) {
82
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aRequest);
83
if (p) p->AdjustPriority(delta);
84
}
85
86
nsLoadGroup::nsLoadGroup()
87
: mForegroundCount(0),
88
mLoadFlags(LOAD_NORMAL),
89
mDefaultLoadFlags(0),
90
mPriority(PRIORITY_NORMAL),
91
mRequests(&sRequestHashOps, sizeof(RequestMapEntry)),
92
mStatus(NS_OK),
93
mIsCanceling(false),
94
mDefaultLoadIsTimed(false),
95
mTimedRequests(0),
96
mCachedRequests(0) {
97
LOG(("LOADGROUP [%p]: Created.\n", this));
98
}
99
100
nsLoadGroup::~nsLoadGroup() {
101
DebugOnly<nsresult> rv = Cancel(NS_BINDING_ABORTED);
102
NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed");
103
104
mDefaultLoadRequest = nullptr;
105
106
if (mRequestContext) {
107
mRequestContextService->RemoveRequestContext(mRequestContext->GetID());
108
}
109
110
LOG(("LOADGROUP [%p]: Destroyed.\n", this));
111
}
112
113
////////////////////////////////////////////////////////////////////////////////
114
// nsISupports methods:
115
116
NS_IMPL_ISUPPORTS(nsLoadGroup, nsILoadGroup, nsILoadGroupChild, nsIRequest,
117
nsISupportsPriority, nsISupportsWeakReference)
118
119
////////////////////////////////////////////////////////////////////////////////
120
// nsIRequest methods:
121
122
NS_IMETHODIMP
123
nsLoadGroup::GetName(nsACString& result) {
124
// XXX is this the right "name" for a load group?
125
126
if (!mDefaultLoadRequest) {
127
result.Truncate();
128
return NS_OK;
129
}
130
131
return mDefaultLoadRequest->GetName(result);
132
}
133
134
NS_IMETHODIMP
135
nsLoadGroup::IsPending(bool* aResult) {
136
*aResult = (mForegroundCount > 0) ? true : false;
137
return NS_OK;
138
}
139
140
NS_IMETHODIMP
141
nsLoadGroup::GetStatus(nsresult* status) {
142
if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest)
143
return mDefaultLoadRequest->GetStatus(status);
144
145
*status = mStatus;
146
return NS_OK;
147
}
148
149
static bool AppendRequestsToArray(PLDHashTable* aTable,
150
nsTArray<nsIRequest*>* aArray) {
151
for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
152
auto e = static_cast<RequestMapEntry*>(iter.Get());
153
nsIRequest* request = e->mKey;
154
NS_ASSERTION(request, "What? Null key in PLDHashTable entry?");
155
156
bool ok = !!aArray->AppendElement(request);
157
if (!ok) {
158
break;
159
}
160
NS_ADDREF(request);
161
}
162
163
if (aArray->Length() != aTable->EntryCount()) {
164
for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
165
NS_RELEASE((*aArray)[i]);
166
}
167
return false;
168
}
169
return true;
170
}
171
172
NS_IMETHODIMP
173
nsLoadGroup::Cancel(nsresult status) {
174
MOZ_ASSERT(NS_IsMainThread());
175
176
NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
177
nsresult rv;
178
uint32_t count = mRequests.EntryCount();
179
180
AutoTArray<nsIRequest*, 8> requests;
181
182
if (!AppendRequestsToArray(&mRequests, &requests)) {
183
return NS_ERROR_OUT_OF_MEMORY;
184
}
185
186
// set the load group status to our cancel status while we cancel
187
// all our requests...once the cancel is done, we'll reset it...
188
//
189
mStatus = status;
190
191
// Set the flag indicating that the loadgroup is being canceled... This
192
// prevents any new channels from being added during the operation.
193
//
194
mIsCanceling = true;
195
196
nsresult firstError = NS_OK;
197
while (count > 0) {
198
nsCOMPtr<nsIRequest> request = requests.ElementAt(--count);
199
200
NS_ASSERTION(request, "NULL request found in list.");
201
202
if (!mRequests.Search(request)) {
203
// |request| was removed already
204
// We need to null out the entry in the request array so we don't try
205
// to notify the observers for this request.
206
nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(count));
207
requests.ElementAt(count) = nullptr;
208
209
continue;
210
}
211
212
if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
213
nsAutoCString nameStr;
214
request->GetName(nameStr);
215
LOG(("LOADGROUP [%p]: Canceling request %p %s.\n", this, request.get(),
216
nameStr.get()));
217
}
218
219
// Cancel the request...
220
rv = request->Cancel(status);
221
222
// Remember the first failure and return it...
223
if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv;
224
225
if (NS_FAILED(RemoveRequestFromHashtable(request, status))) {
226
// It's possible that request->Cancel causes the request to be removed
227
// from the loadgroup causing RemoveRequestFromHashtable to fail.
228
// In that case we shouldn't call NotifyRemovalObservers or decrement
229
// mForegroundCount since that has already happened.
230
nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(count));
231
requests.ElementAt(count) = nullptr;
232
233
continue;
234
}
235
}
236
237
for (count = requests.Length(); count > 0;) {
238
nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count));
239
(void)NotifyRemovalObservers(request, status);
240
}
241
242
if (mRequestContext) {
243
Unused << mRequestContext->CancelTailPendingRequests(status);
244
}
245
246
#if defined(DEBUG)
247
NS_ASSERTION(mRequests.EntryCount() == 0, "Request list is not empty.");
248
NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");
249
#endif
250
251
mStatus = NS_OK;
252
mIsCanceling = false;
253
254
return firstError;
255
}
256
257
NS_IMETHODIMP
258
nsLoadGroup::Suspend() {
259
nsresult rv, firstError;
260
uint32_t count = mRequests.EntryCount();
261
262
AutoTArray<nsIRequest*, 8> requests;
263
264
if (!AppendRequestsToArray(&mRequests, &requests)) {
265
return NS_ERROR_OUT_OF_MEMORY;
266
}
267
268
firstError = NS_OK;
269
//
270
// Operate the elements from back to front so that if items get
271
// get removed from the list it won't affect our iteration
272
//
273
while (count > 0) {
274
nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count));
275
276
NS_ASSERTION(request, "NULL request found in list.");
277
if (!request) continue;
278
279
if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
280
nsAutoCString nameStr;
281
request->GetName(nameStr);
282
LOG(("LOADGROUP [%p]: Suspending request %p %s.\n", this, request.get(),
283
nameStr.get()));
284
}
285
286
// Suspend the request...
287
rv = request->Suspend();
288
289
// Remember the first failure and return it...
290
if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv;
291
}
292
293
return firstError;
294
}
295
296
NS_IMETHODIMP
297
nsLoadGroup::Resume() {
298
nsresult rv, firstError;
299
uint32_t count = mRequests.EntryCount();
300
301
AutoTArray<nsIRequest*, 8> requests;
302
303
if (!AppendRequestsToArray(&mRequests, &requests)) {
304
return NS_ERROR_OUT_OF_MEMORY;
305
}
306
307
firstError = NS_OK;
308
//
309
// Operate the elements from back to front so that if items get
310
// get removed from the list it won't affect our iteration
311
//
312
while (count > 0) {
313
nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count));
314
315
NS_ASSERTION(request, "NULL request found in list.");
316
if (!request) continue;
317
318
if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
319
nsAutoCString nameStr;
320
request->GetName(nameStr);
321
LOG(("LOADGROUP [%p]: Resuming request %p %s.\n", this, request.get(),
322
nameStr.get()));
323
}
324
325
// Resume the request...
326
rv = request->Resume();
327
328
// Remember the first failure and return it...
329
if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv;
330
}
331
332
return firstError;
333
}
334
335
NS_IMETHODIMP
336
nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags) {
337
*aLoadFlags = mLoadFlags;
338
return NS_OK;
339
}
340
341
NS_IMETHODIMP
342
nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags) {
343
mLoadFlags = aLoadFlags;
344
return NS_OK;
345
}
346
347
NS_IMETHODIMP
348
nsLoadGroup::GetLoadGroup(nsILoadGroup** loadGroup) {
349
nsCOMPtr<nsILoadGroup> result = mLoadGroup;
350
result.forget(loadGroup);
351
return NS_OK;
352
}
353
354
NS_IMETHODIMP
355
nsLoadGroup::SetLoadGroup(nsILoadGroup* loadGroup) {
356
mLoadGroup = loadGroup;
357
return NS_OK;
358
}
359
360
////////////////////////////////////////////////////////////////////////////////
361
// nsILoadGroup methods:
362
363
NS_IMETHODIMP
364
nsLoadGroup::GetDefaultLoadRequest(nsIRequest** aRequest) {
365
nsCOMPtr<nsIRequest> result = mDefaultLoadRequest;
366
result.forget(aRequest);
367
return NS_OK;
368
}
369
370
NS_IMETHODIMP
371
nsLoadGroup::SetDefaultLoadRequest(nsIRequest* aRequest) {
372
LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this,
373
aRequest));
374
375
mDefaultLoadRequest = aRequest;
376
// Inherit the group load flags from the default load request
377
if (mDefaultLoadRequest) {
378
mDefaultLoadRequest->GetLoadFlags(&mLoadFlags);
379
//
380
// Mask off any bits that are not part of the nsIRequest flags.
381
// in particular, nsIChannel::LOAD_DOCUMENT_URI...
382
//
383
mLoadFlags &= nsIRequest::LOAD_REQUESTMASK;
384
385
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(aRequest);
386
mDefaultLoadIsTimed = timedChannel != nullptr;
387
if (mDefaultLoadIsTimed) {
388
timedChannel->GetChannelCreation(&mDefaultRequestCreationTime);
389
timedChannel->SetTimingEnabled(true);
390
}
391
}
392
// Else, do not change the group's load flags (see bug 95981)
393
return NS_OK;
394
}
395
396
NS_IMETHODIMP
397
nsLoadGroup::AddRequest(nsIRequest* request, nsISupports* ctxt) {
398
nsresult rv;
399
400
if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
401
nsAutoCString nameStr;
402
request->GetName(nameStr);
403
LOG(("LOADGROUP [%p]: Adding request %p %s (count=%d).\n", this, request,
404
nameStr.get(), mRequests.EntryCount()));
405
}
406
407
NS_ASSERTION(!mRequests.Search(request),
408
"Entry added to loadgroup twice, don't do that");
409
410
//
411
// Do not add the channel, if the loadgroup is being canceled...
412
//
413
if (mIsCanceling) {
414
LOG(
415
("LOADGROUP [%p]: AddChannel() ABORTED because LoadGroup is"
416
" being canceled!!\n",
417
this));
418
419
return NS_BINDING_ABORTED;
420
}
421
422
nsLoadFlags flags;
423
// if the request is the default load request or if the default load
424
// request is null, then the load group should inherit its load flags from
425
// the request, but also we need to enforce defaultLoadFlags.
426
if (mDefaultLoadRequest == request || !mDefaultLoadRequest) {
427
rv = MergeDefaultLoadFlags(request, flags);
428
} else {
429
rv = MergeLoadFlags(request, flags);
430
}
431
if (NS_FAILED(rv)) return rv;
432
433
//
434
// Add the request to the list of active requests...
435
//
436
437
auto entry = static_cast<RequestMapEntry*>(mRequests.Add(request, fallible));
438
if (!entry) {
439
return NS_ERROR_OUT_OF_MEMORY;
440
}
441
442
if (mPriority != 0) RescheduleRequest(request, mPriority);
443
444
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(request);
445
if (timedChannel) timedChannel->SetTimingEnabled(true);
446
447
if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
448
// Update the count of foreground URIs..
449
mForegroundCount += 1;
450
451
//
452
// Fire the OnStartRequest notification out to the observer...
453
//
454
// If the notification fails then DO NOT add the request to
455
// the load group.
456
//
457
nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
458
if (observer) {
459
LOG(
460
("LOADGROUP [%p]: Firing OnStartRequest for request %p."
461
"(foreground count=%d).\n",
462
this, request, mForegroundCount));
463
464
rv = observer->OnStartRequest(request);
465
if (NS_FAILED(rv)) {
466
LOG(("LOADGROUP [%p]: OnStartRequest for request %p FAILED.\n", this,
467
request));
468
//
469
// The URI load has been canceled by the observer. Clean up
470
// the damage...
471
//
472
473
mRequests.Remove(request);
474
475
rv = NS_OK;
476
477
mForegroundCount -= 1;
478
}
479
}
480
481
// Ensure that we're part of our loadgroup while pending
482
if (mForegroundCount == 1 && mLoadGroup) {
483
mLoadGroup->AddRequest(this, nullptr);
484
}
485
}
486
487
return rv;
488
}
489
490
NS_IMETHODIMP
491
nsLoadGroup::RemoveRequest(nsIRequest* request, nsISupports* ctxt,
492
nsresult aStatus) {
493
// Make sure we have a owning reference to the request we're about
494
// to remove.
495
nsCOMPtr<nsIRequest> kungFuDeathGrip(request);
496
497
nsresult rv = RemoveRequestFromHashtable(request, aStatus);
498
if (NS_FAILED(rv)) {
499
return rv;
500
}
501
502
return NotifyRemovalObservers(request, aStatus);
503
}
504
505
nsresult nsLoadGroup::RemoveRequestFromHashtable(nsIRequest* request,
506
nsresult aStatus) {
507
NS_ENSURE_ARG_POINTER(request);
508
nsresult rv;
509
510
if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
511
nsAutoCString nameStr;
512
request->GetName(nameStr);
513
LOG(("LOADGROUP [%p]: Removing request %p %s status %" PRIx32
514
" (count=%d).\n",
515
this, request, nameStr.get(), static_cast<uint32_t>(aStatus),
516
mRequests.EntryCount() - 1));
517
}
518
519
//
520
// Remove the request from the group. If this fails, it means that
521
// the request was *not* in the group so do not update the foreground
522
// count or it will get messed up...
523
//
524
auto entry = static_cast<RequestMapEntry*>(mRequests.Search(request));
525
526
if (!entry) {
527
LOG(("LOADGROUP [%p]: Unable to remove request %p. Not in group!\n", this,
528
request));
529
530
return NS_ERROR_FAILURE;
531
}
532
533
mRequests.RemoveEntry(entry);
534
535
// Collect telemetry stats only when default request is a timed channel.
536
// Don't include failed requests in the timing statistics.
537
if (mDefaultLoadIsTimed && NS_SUCCEEDED(aStatus)) {
538
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(request);
539
if (timedChannel) {
540
// Figure out if this request was served from the cache
541
++mTimedRequests;
542
TimeStamp timeStamp;
543
rv = timedChannel->GetCacheReadStart(&timeStamp);
544
if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
545
++mCachedRequests;
546
}
547
548
rv = timedChannel->GetAsyncOpen(&timeStamp);
549
if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
550
Telemetry::AccumulateTimeDelta(
551
Telemetry::HTTP_SUBITEM_OPEN_LATENCY_TIME,
552
mDefaultRequestCreationTime, timeStamp);
553
}
554
555
rv = timedChannel->GetResponseStart(&timeStamp);
556
if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
557
Telemetry::AccumulateTimeDelta(
558
Telemetry::HTTP_SUBITEM_FIRST_BYTE_LATENCY_TIME,
559
mDefaultRequestCreationTime, timeStamp);
560
}
561
562
TelemetryReportChannel(timedChannel, false);
563
}
564
}
565
566
if (mRequests.EntryCount() == 0) {
567
TelemetryReport();
568
}
569
570
return NS_OK;
571
}
572
573
nsresult nsLoadGroup::NotifyRemovalObservers(nsIRequest* request,
574
nsresult aStatus) {
575
NS_ENSURE_ARG_POINTER(request);
576
// Undo any group priority delta...
577
if (mPriority != 0) RescheduleRequest(request, -mPriority);
578
579
nsLoadFlags flags;
580
nsresult rv = request->GetLoadFlags(&flags);
581
if (NS_FAILED(rv)) return rv;
582
583
if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
584
NS_ASSERTION(mForegroundCount > 0, "ForegroundCount messed up");
585
mForegroundCount -= 1;
586
587
// Fire the OnStopRequest out to the observer...
588
nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
589
if (observer) {
590
LOG(
591
("LOADGROUP [%p]: Firing OnStopRequest for request %p."
592
"(foreground count=%d).\n",
593
this, request, mForegroundCount));
594
595
rv = observer->OnStopRequest(request, aStatus);
596
597
if (NS_FAILED(rv)) {
598
LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this,
599
request));
600
}
601
}
602
603
// If that was the last request -> remove ourselves from loadgroup
604
if (mForegroundCount == 0 && mLoadGroup) {
605
mLoadGroup->RemoveRequest(this, nullptr, aStatus);
606
}
607
}
608
609
return rv;
610
}
611
612
NS_IMETHODIMP
613
nsLoadGroup::GetRequests(nsISimpleEnumerator** aRequests) {
614
nsCOMArray<nsIRequest> requests;
615
requests.SetCapacity(mRequests.EntryCount());
616
617
for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
618
auto e = static_cast<RequestMapEntry*>(iter.Get());
619
requests.AppendObject(e->mKey);
620
}
621
622
return NS_NewArrayEnumerator(aRequests, requests, NS_GET_IID(nsIRequest));
623
}
624
625
NS_IMETHODIMP
626
nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) {
627
mObserver = do_GetWeakReference(aObserver);
628
return NS_OK;
629
}
630
631
NS_IMETHODIMP
632
nsLoadGroup::GetGroupObserver(nsIRequestObserver** aResult) {
633
nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
634
observer.forget(aResult);
635
return NS_OK;
636
}
637
638
NS_IMETHODIMP
639
nsLoadGroup::GetActiveCount(uint32_t* aResult) {
640
*aResult = mForegroundCount;
641
return NS_OK;
642
}
643
644
NS_IMETHODIMP
645
nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
646
NS_ENSURE_ARG_POINTER(aCallbacks);
647
nsCOMPtr<nsIInterfaceRequestor> callbacks = mCallbacks;
648
callbacks.forget(aCallbacks);
649
return NS_OK;
650
}
651
652
NS_IMETHODIMP
653
nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
654
mCallbacks = aCallbacks;
655
return NS_OK;
656
}
657
658
NS_IMETHODIMP
659
nsLoadGroup::GetRequestContextID(uint64_t* aRCID) {
660
if (!mRequestContext) {
661
return NS_ERROR_NOT_AVAILABLE;
662
}
663
*aRCID = mRequestContext->GetID();
664
return NS_OK;
665
}
666
667
////////////////////////////////////////////////////////////////////////////////
668
// nsILoadGroupChild methods:
669
670
NS_IMETHODIMP
671
nsLoadGroup::GetParentLoadGroup(nsILoadGroup** aParentLoadGroup) {
672
*aParentLoadGroup = nullptr;
673
nsCOMPtr<nsILoadGroup> parent = do_QueryReferent(mParentLoadGroup);
674
if (!parent) return NS_OK;
675
parent.forget(aParentLoadGroup);
676
return NS_OK;
677
}
678
679
NS_IMETHODIMP
680
nsLoadGroup::SetParentLoadGroup(nsILoadGroup* aParentLoadGroup) {
681
mParentLoadGroup = do_GetWeakReference(aParentLoadGroup);
682
return NS_OK;
683
}
684
685
NS_IMETHODIMP
686
nsLoadGroup::GetChildLoadGroup(nsILoadGroup** aChildLoadGroup) {
687
NS_ADDREF(*aChildLoadGroup = this);
688
return NS_OK;
689
}
690
691
NS_IMETHODIMP
692
nsLoadGroup::GetRootLoadGroup(nsILoadGroup** aRootLoadGroup) {
693
// first recursively try the root load group of our parent
694
nsCOMPtr<nsILoadGroupChild> ancestor = do_QueryReferent(mParentLoadGroup);
695
if (ancestor) return ancestor->GetRootLoadGroup(aRootLoadGroup);
696
697
// next recursively try the root load group of our own load grop
698
ancestor = do_QueryInterface(mLoadGroup);
699
if (ancestor) return ancestor->GetRootLoadGroup(aRootLoadGroup);
700
701
// finally just return this
702
NS_ADDREF(*aRootLoadGroup = this);
703
return NS_OK;
704
}
705
706
////////////////////////////////////////////////////////////////////////////////
707
// nsISupportsPriority methods:
708
709
NS_IMETHODIMP
710
nsLoadGroup::GetPriority(int32_t* aValue) {
711
*aValue = mPriority;
712
return NS_OK;
713
}
714
715
NS_IMETHODIMP
716
nsLoadGroup::SetPriority(int32_t aValue) {
717
return AdjustPriority(aValue - mPriority);
718
}
719
720
NS_IMETHODIMP
721
nsLoadGroup::AdjustPriority(int32_t aDelta) {
722
// Update the priority for each request that supports nsISupportsPriority
723
if (aDelta != 0) {
724
mPriority += aDelta;
725
for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
726
auto e = static_cast<RequestMapEntry*>(iter.Get());
727
RescheduleRequest(e->mKey, aDelta);
728
}
729
}
730
return NS_OK;
731
}
732
733
NS_IMETHODIMP
734
nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags) {
735
*aFlags = mDefaultLoadFlags;
736
return NS_OK;
737
}
738
739
NS_IMETHODIMP
740
nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags) {
741
mDefaultLoadFlags = aFlags;
742
return NS_OK;
743
}
744
745
////////////////////////////////////////////////////////////////////////////////
746
747
void nsLoadGroup::TelemetryReport() {
748
nsresult defaultStatus = NS_ERROR_INVALID_ARG;
749
// We should only report HTTP_PAGE_* telemetry if the defaultRequest was
750
// actually successful.
751
if (mDefaultLoadRequest) {
752
mDefaultLoadRequest->GetStatus(&defaultStatus);
753
}
754
if (mDefaultLoadIsTimed && NS_SUCCEEDED(defaultStatus)) {
755
Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE, mTimedRequests);
756
if (mTimedRequests) {
757
Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE_FROM_CACHE,
758
mCachedRequests * 100 / mTimedRequests);
759
}
760
761
nsCOMPtr<nsITimedChannel> timedChannel =
762
do_QueryInterface(mDefaultLoadRequest);
763
if (timedChannel) TelemetryReportChannel(timedChannel, true);
764
}
765
766
mTimedRequests = 0;
767
mCachedRequests = 0;
768
mDefaultLoadIsTimed = false;
769
}
770
771
void nsLoadGroup::TelemetryReportChannel(nsITimedChannel* aTimedChannel,
772
bool aDefaultRequest) {
773
nsresult rv;
774
bool timingEnabled;
775
rv = aTimedChannel->GetTimingEnabled(&timingEnabled);
776
if (NS_FAILED(rv) || !timingEnabled) return;
777
778
TimeStamp asyncOpen;
779
rv = aTimedChannel->GetAsyncOpen(&asyncOpen);
780
// We do not check !asyncOpen.IsNull() bellow, prevent ASSERTIONs this way
781
if (NS_FAILED(rv) || asyncOpen.IsNull()) return;
782
783
TimeStamp cacheReadStart;
784
rv = aTimedChannel->GetCacheReadStart(&cacheReadStart);
785
if (NS_FAILED(rv)) return;
786
787
TimeStamp cacheReadEnd;
788
rv = aTimedChannel->GetCacheReadEnd(&cacheReadEnd);
789
if (NS_FAILED(rv)) return;
790
791
TimeStamp domainLookupStart;
792
rv = aTimedChannel->GetDomainLookupStart(&domainLookupStart);
793
if (NS_FAILED(rv)) return;
794
795
TimeStamp domainLookupEnd;
796
rv = aTimedChannel->GetDomainLookupEnd(&domainLookupEnd);
797
if (NS_FAILED(rv)) return;
798
799
TimeStamp connectStart;
800
rv = aTimedChannel->GetConnectStart(&connectStart);
801
if (NS_FAILED(rv)) return;
802
803
TimeStamp secureConnectionStart;
804
rv = aTimedChannel->GetSecureConnectionStart(&secureConnectionStart);
805
if (NS_FAILED(rv)) return;
806
807
TimeStamp connectEnd;
808
rv = aTimedChannel->GetConnectEnd(&connectEnd);
809
if (NS_FAILED(rv)) return;
810
811
TimeStamp requestStart;
812
rv = aTimedChannel->GetRequestStart(&requestStart);
813
if (NS_FAILED(rv)) return;
814
815
TimeStamp responseStart;
816
rv = aTimedChannel->GetResponseStart(&responseStart);
817
if (NS_FAILED(rv)) return;
818
819
TimeStamp responseEnd;
820
rv = aTimedChannel->GetResponseEnd(&responseEnd);
821
if (NS_FAILED(rv)) return;
822
823
#define HTTP_REQUEST_HISTOGRAMS(prefix) \
824
if (!domainLookupStart.IsNull()) { \
825
Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_DNS_ISSUE_TIME, \
826
asyncOpen, domainLookupStart); \
827
} \
828
\
829
if (!domainLookupStart.IsNull() && !domainLookupEnd.IsNull()) { \
830
Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_DNS_LOOKUP_TIME, \
831
domainLookupStart, domainLookupEnd); \
832
} \
833
\
834
if (!secureConnectionStart.IsNull() && !connectEnd.IsNull()) { \
835
Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_TLS_HANDSHAKE, \
836
secureConnectionStart, connectEnd); \
837
} \
838
\
839
if (!connectStart.IsNull() && !connectEnd.IsNull()) { \
840
Telemetry::AccumulateTimeDelta( \
841
Telemetry::HTTP_##prefix##_TCP_CONNECTION_2, connectStart, \
842
connectEnd); \
843
} \
844
\
845
if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
846
Telemetry::AccumulateTimeDelta( \
847
Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_SENT, asyncOpen, \
848
requestStart); \
849
\
850
Telemetry::AccumulateTimeDelta( \
851
Telemetry::HTTP_##prefix##_FIRST_SENT_TO_LAST_RECEIVED, requestStart, \
852
responseEnd); \
853
\
854
if (cacheReadStart.IsNull() && !responseStart.IsNull()) { \
855
Telemetry::AccumulateTimeDelta( \
856
Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_RECEIVED, asyncOpen, \
857
responseStart); \
858
} \
859
} \
860
\
861
if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) { \
862
Telemetry::AccumulateTimeDelta( \
863
Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_FROM_CACHE_V2, asyncOpen, \
864
cacheReadStart); \
865
\
866
Telemetry::AccumulateTimeDelta( \
867
Telemetry::HTTP_##prefix##_CACHE_READ_TIME_V2, cacheReadStart, \
868
cacheReadEnd); \
869
\
870
if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
871
Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_REVALIDATION, \
872
requestStart, responseEnd); \
873
} \
874
} \
875
\
876
if (!cacheReadEnd.IsNull()) { \
877
Telemetry::AccumulateTimeDelta( \
878
Telemetry::HTTP_##prefix##_COMPLETE_LOAD_V2, asyncOpen, cacheReadEnd); \
879
Telemetry::AccumulateTimeDelta( \
880
Telemetry::HTTP_##prefix##_COMPLETE_LOAD_CACHED_V2, asyncOpen, \
881
cacheReadEnd); \
882
} else if (!responseEnd.IsNull()) { \
883
Telemetry::AccumulateTimeDelta( \
884
Telemetry::HTTP_##prefix##_COMPLETE_LOAD_V2, asyncOpen, responseEnd); \
885
Telemetry::AccumulateTimeDelta( \
886
Telemetry::HTTP_##prefix##_COMPLETE_LOAD_NET_V2, asyncOpen, \
887
responseEnd); \
888
}
889
890
if (aDefaultRequest) {
891
HTTP_REQUEST_HISTOGRAMS(PAGE)
892
} else {
893
HTTP_REQUEST_HISTOGRAMS(SUB)
894
}
895
#undef HTTP_REQUEST_HISTOGRAMS
896
}
897
898
nsresult nsLoadGroup::MergeLoadFlags(nsIRequest* aRequest,
899
nsLoadFlags& outFlags) {
900
nsresult rv;
901
nsLoadFlags flags, oldFlags;
902
903
rv = aRequest->GetLoadFlags(&flags);
904
if (NS_FAILED(rv)) {
905
return rv;
906
}
907
908
oldFlags = flags;
909
910
// Inherit the following bits...
911
flags |= (mLoadFlags &
912
(LOAD_BACKGROUND | LOAD_BYPASS_CACHE | LOAD_FROM_CACHE |
913
VALIDATE_ALWAYS | VALIDATE_ONCE_PER_SESSION | VALIDATE_NEVER));
914
915
// ... and force the default flags.
916
flags |= mDefaultLoadFlags;
917
918
if (flags != oldFlags) {
919
rv = aRequest->SetLoadFlags(flags);
920
}
921
922
outFlags = flags;
923
return rv;
924
}
925
926
nsresult nsLoadGroup::MergeDefaultLoadFlags(nsIRequest* aRequest,
927
nsLoadFlags& outFlags) {
928
nsresult rv;
929
nsLoadFlags flags, oldFlags;
930
931
rv = aRequest->GetLoadFlags(&flags);
932
if (NS_FAILED(rv)) {
933
return rv;
934
}
935
936
oldFlags = flags;
937
// ... and force the default flags.
938
flags |= mDefaultLoadFlags;
939
940
if (flags != oldFlags) {
941
rv = aRequest->SetLoadFlags(flags);
942
}
943
outFlags = flags;
944
return rv;
945
}
946
947
nsresult nsLoadGroup::Init() {
948
mRequestContextService = RequestContextService::GetOrCreate();
949
if (mRequestContextService) {
950
Unused << mRequestContextService->NewRequestContext(
951
getter_AddRefs(mRequestContext));
952
}
953
954
return NS_OK;
955
}
956
957
} // namespace net
958
} // namespace mozilla
959
960
#undef LOG