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