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/. */
7 #include "mozilla/DebugOnly.h"
9 #include "nsLoadGroup.h"
11 #include "nsArrayEnumerator.h"
12 #include "nsCOMArray.h"
14 #include "nsContentUtils.h"
15 #include "mozilla/Logging.h"
18 #include "nsIHttpChannelInternal.h"
19 #include "nsITimedChannel.h"
20 #include "nsIInterfaceRequestor.h"
21 #include "nsIRequestObserver.h"
22 #include "CacheObserver.h"
23 #include "MainThreadUtils.h"
24 #include "RequestContextService.h"
25 #include "mozilla/glean/NetwerkMetrics.h"
26 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
27 #include "mozilla/StoragePrincipalHelper.h"
28 #include "mozilla/Unused.h"
29 #include "mozilla/net/NeckoCommon.h"
30 #include "mozilla/net/NeckoChild.h"
31 #include "mozilla/StaticPrefs_network.h"
37 // Log module for nsILoadGroup logging...
39 // To enable logging (see prlog.h for full details):
41 // set MOZ_LOG=LoadGroup:5
42 // set MOZ_LOG_FILE=network.log
44 // This enables LogLevel::Debug level information and places all output in
45 // the file network.log.
47 static LazyLogModule
gLoadGroupLog("LoadGroup");
49 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
51 ////////////////////////////////////////////////////////////////////////////////
53 class RequestMapEntry
: public PLDHashEntryHdr
{
55 explicit RequestMapEntry(nsIRequest
* aRequest
) : mKey(aRequest
) {}
57 nsCOMPtr
<nsIRequest
> mKey
;
60 static bool RequestHashMatchEntry(const PLDHashEntryHdr
* entry
,
62 const RequestMapEntry
* e
= static_cast<const RequestMapEntry
*>(entry
);
63 const nsIRequest
* request
= static_cast<const nsIRequest
*>(key
);
65 return e
->mKey
== request
;
68 static void RequestHashClearEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
) {
69 RequestMapEntry
* e
= static_cast<RequestMapEntry
*>(entry
);
71 // An entry is being cleared, let the entry do its own cleanup.
72 e
->~RequestMapEntry();
75 static void RequestHashInitEntry(PLDHashEntryHdr
* entry
, const void* key
) {
76 const nsIRequest
* const_request
= static_cast<const nsIRequest
*>(key
);
77 nsIRequest
* request
= const_cast<nsIRequest
*>(const_request
);
79 // Initialize the entry with placement new
80 new (entry
) RequestMapEntry(request
);
83 static const PLDHashTableOps sRequestHashOps
= {
84 PLDHashTable::HashVoidPtrKeyStub
, RequestHashMatchEntry
,
85 PLDHashTable::MoveEntryStub
, RequestHashClearEntry
, RequestHashInitEntry
};
87 static void RescheduleRequest(nsIRequest
* aRequest
, int32_t delta
) {
88 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(aRequest
);
89 if (p
) p
->AdjustPriority(delta
);
92 nsLoadGroup::nsLoadGroup()
93 : mRequests(&sRequestHashOps
, sizeof(RequestMapEntry
)) {
94 LOG(("LOADGROUP [%p]: Created.\n", this));
97 nsLoadGroup::~nsLoadGroup() {
98 DebugOnly
<nsresult
> rv
=
99 CancelWithReason(NS_BINDING_ABORTED
, "nsLoadGroup::~nsLoadGroup"_ns
);
100 NS_ASSERTION(NS_SUCCEEDED(rv
), "Cancel failed");
102 mDefaultLoadRequest
= nullptr;
104 if (mRequestContext
&& !mExternalRequestContext
) {
105 mRequestContextService
->RemoveRequestContext(mRequestContext
->GetID());
106 if (IsNeckoChild() && gNeckoChild
&& gNeckoChild
->CanSend()) {
107 gNeckoChild
->SendRemoveRequestContext(mRequestContext
->GetID());
111 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
113 Unused
<< os
->RemoveObserver(this, "last-pb-context-exited");
116 LOG(("LOADGROUP [%p]: Destroyed.\n", this));
119 ////////////////////////////////////////////////////////////////////////////////
120 // nsISupports methods:
122 NS_IMPL_ISUPPORTS(nsLoadGroup
, nsILoadGroup
, nsILoadGroupChild
, nsIRequest
,
123 nsISupportsPriority
, nsISupportsWeakReference
, nsIObserver
)
125 ////////////////////////////////////////////////////////////////////////////////
126 // nsIRequest methods:
129 nsLoadGroup::GetName(nsACString
& result
) {
130 // XXX is this the right "name" for a load group?
132 if (!mDefaultLoadRequest
) {
137 return mDefaultLoadRequest
->GetName(result
);
141 nsLoadGroup::IsPending(bool* aResult
) {
142 *aResult
= mForegroundCount
> 0;
147 nsLoadGroup::GetStatus(nsresult
* status
) {
148 if (NS_SUCCEEDED(mStatus
) && mDefaultLoadRequest
) {
149 return mDefaultLoadRequest
->GetStatus(status
);
156 static bool AppendRequestsToArray(PLDHashTable
* aTable
,
157 nsTArray
<nsIRequest
*>* aArray
) {
158 for (auto iter
= aTable
->Iter(); !iter
.Done(); iter
.Next()) {
159 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
160 nsIRequest
* request
= e
->mKey
;
161 MOZ_DIAGNOSTIC_ASSERT(request
, "Null key in mRequests PLDHashTable entry");
163 // XXX(Bug 1631371) Check if this should use a fallible operation as it
164 // pretended earlier.
165 aArray
->AppendElement(request
);
169 if (aArray
->Length() != aTable
->EntryCount()) {
170 for (uint32_t i
= 0, len
= aArray
->Length(); i
< len
; ++i
) {
171 NS_RELEASE((*aArray
)[i
]);
178 NS_IMETHODIMP
nsLoadGroup::SetCanceledReason(const nsACString
& aReason
) {
179 return SetCanceledReasonImpl(aReason
);
182 NS_IMETHODIMP
nsLoadGroup::GetCanceledReason(nsACString
& aReason
) {
183 return GetCanceledReasonImpl(aReason
);
186 NS_IMETHODIMP
nsLoadGroup::CancelWithReason(nsresult aStatus
,
187 const nsACString
& aReason
) {
188 return CancelWithReasonImpl(aStatus
, aReason
);
192 nsLoadGroup::Cancel(nsresult status
) {
193 MOZ_ASSERT(NS_IsMainThread());
195 NS_ASSERTION(NS_FAILED(status
), "shouldn't cancel with a success code");
197 uint32_t count
= mRequests
.EntryCount();
199 AutoTArray
<nsIRequest
*, 8> requests
;
201 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
202 return NS_ERROR_OUT_OF_MEMORY
;
205 // set the load group status to our cancel status while we cancel
206 // all our requests...once the cancel is done, we'll reset it...
210 // Set the flag indicating that the loadgroup is being canceled... This
211 // prevents any new channels from being added during the operation.
215 nsresult firstError
= NS_OK
;
217 nsCOMPtr
<nsIRequest
> request
= requests
.ElementAt(--count
);
219 NS_ASSERTION(request
, "NULL request found in list.");
221 if (!mRequests
.Search(request
)) {
222 // |request| was removed already
223 // We need to null out the entry in the request array so we don't try
224 // to notify the observers for this request.
225 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(count
));
226 requests
.ElementAt(count
) = nullptr;
231 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
232 nsAutoCString nameStr
;
233 request
->GetName(nameStr
);
234 LOG(("LOADGROUP [%p]: Canceling request %p %s.\n", this, request
.get(),
238 // Cancel the request...
239 rv
= request
->CancelWithReason(status
, mCanceledReason
);
241 // Remember the first failure and return it...
242 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
244 if (NS_FAILED(RemoveRequestFromHashtable(request
, status
))) {
245 // It's possible that request->Cancel causes the request to be removed
246 // from the loadgroup causing RemoveRequestFromHashtable to fail.
247 // In that case we shouldn't call NotifyRemovalObservers or decrement
248 // mForegroundCount since that has already happened.
249 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(count
));
250 requests
.ElementAt(count
) = nullptr;
256 for (count
= requests
.Length(); count
> 0;) {
257 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
258 (void)NotifyRemovalObservers(request
, status
);
261 if (mRequestContext
) {
262 Unused
<< mRequestContext
->CancelTailPendingRequests(status
);
266 NS_ASSERTION(mRequests
.EntryCount() == 0, "Request list is not empty.");
267 NS_ASSERTION(mForegroundCount
== 0, "Foreground URLs are active.");
271 mIsCanceling
= false;
277 nsLoadGroup::Suspend() {
278 nsresult rv
, firstError
;
279 uint32_t count
= mRequests
.EntryCount();
281 AutoTArray
<nsIRequest
*, 8> requests
;
283 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
284 return NS_ERROR_OUT_OF_MEMORY
;
289 // Operate the elements from back to front so that if items get
290 // get removed from the list it won't affect our iteration
293 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
295 NS_ASSERTION(request
, "NULL request found in list.");
296 if (!request
) continue;
298 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
299 nsAutoCString nameStr
;
300 request
->GetName(nameStr
);
301 LOG(("LOADGROUP [%p]: Suspending request %p %s.\n", this, request
.get(),
305 // Suspend the request...
306 rv
= request
->Suspend();
308 // Remember the first failure and return it...
309 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
316 nsLoadGroup::Resume() {
317 nsresult rv
, firstError
;
318 uint32_t count
= mRequests
.EntryCount();
320 AutoTArray
<nsIRequest
*, 8> requests
;
322 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
323 return NS_ERROR_OUT_OF_MEMORY
;
328 // Operate the elements from back to front so that if items get
329 // get removed from the list it won't affect our iteration
332 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
334 NS_ASSERTION(request
, "NULL request found in list.");
335 if (!request
) continue;
337 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
338 nsAutoCString nameStr
;
339 request
->GetName(nameStr
);
340 LOG(("LOADGROUP [%p]: Resuming request %p %s.\n", this, request
.get(),
344 // Resume the request...
345 rv
= request
->Resume();
347 // Remember the first failure and return it...
348 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
355 nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags
) {
356 *aLoadFlags
= mLoadFlags
;
361 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags
) {
362 mLoadFlags
= aLoadFlags
;
367 nsLoadGroup::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
368 return GetTRRModeImpl(aTRRMode
);
372 nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
373 return SetTRRModeImpl(aTRRMode
);
377 nsLoadGroup::GetLoadGroup(nsILoadGroup
** loadGroup
) {
378 nsCOMPtr
<nsILoadGroup
> result
= mLoadGroup
;
379 result
.forget(loadGroup
);
384 nsLoadGroup::SetLoadGroup(nsILoadGroup
* loadGroup
) {
385 mLoadGroup
= loadGroup
;
389 ////////////////////////////////////////////////////////////////////////////////
390 // nsILoadGroup methods:
393 nsLoadGroup::GetDefaultLoadRequest(nsIRequest
** aRequest
) {
394 nsCOMPtr
<nsIRequest
> result
= mDefaultLoadRequest
;
395 result
.forget(aRequest
);
400 nsLoadGroup::SetDefaultLoadRequest(nsIRequest
* aRequest
) {
401 LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this,
404 mDefaultLoadRequest
= aRequest
;
405 // Inherit the group load flags from the default load request
406 if (mDefaultLoadRequest
) {
407 mDefaultLoadRequest
->GetLoadFlags(&mLoadFlags
);
409 // Mask off any bits that are not part of the nsIRequest flags.
410 // in particular, nsIChannel::LOAD_DOCUMENT_URI...
412 mLoadFlags
&= nsIRequest::LOAD_REQUESTMASK
;
414 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(aRequest
);
415 mDefaultLoadIsTimed
= timedChannel
!= nullptr;
416 if (mDefaultLoadIsTimed
) {
417 timedChannel
->GetChannelCreation(&mDefaultRequestCreationTime
);
420 // Else, do not change the group's load flags (see bug 95981)
425 nsLoadGroup::AddRequest(nsIRequest
* request
, nsISupports
* ctxt
) {
428 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
429 nsAutoCString nameStr
;
430 request
->GetName(nameStr
);
431 LOG(("LOADGROUP [%p]: Adding request %p %s (count=%d).\n", this, request
,
432 nameStr
.get(), mRequests
.EntryCount()));
435 NS_ASSERTION(!mRequests
.Search(request
),
436 "Entry added to loadgroup twice, don't do that");
439 // Do not add the channel, if the loadgroup is being canceled...
443 ("LOADGROUP [%p]: AddChannel() ABORTED because LoadGroup is"
444 " being canceled!!\n",
447 return NS_BINDING_ABORTED
;
451 // if the request is the default load request or if the default load
452 // request is null, then the load group should inherit its load flags from
453 // the request, but also we need to enforce defaultLoadFlags.
454 if (mDefaultLoadRequest
== request
|| !mDefaultLoadRequest
) {
455 rv
= MergeDefaultLoadFlags(request
, flags
);
457 rv
= MergeLoadFlags(request
, flags
);
459 if (NS_FAILED(rv
)) return rv
;
462 // Add the request to the list of active requests...
465 auto* entry
= static_cast<RequestMapEntry
*>(mRequests
.Add(request
, fallible
));
467 return NS_ERROR_OUT_OF_MEMORY
;
470 if (mPriority
!= 0) RescheduleRequest(request
, mPriority
);
472 bool foreground
= !(flags
& nsIRequest::LOAD_BACKGROUND
);
474 // Update the count of foreground URIs..
475 mForegroundCount
+= 1;
478 if (foreground
|| mNotifyObserverAboutBackgroundRequests
) {
480 // Fire the OnStartRequest notification out to the observer...
482 // If the notification fails then DO NOT add the request to
485 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
488 ("LOADGROUP [%p]: Firing OnStartRequest for request %p."
489 "(foreground count=%d).\n",
490 this, request
, mForegroundCount
));
492 rv
= observer
->OnStartRequest(request
);
494 LOG(("LOADGROUP [%p]: OnStartRequest for request %p FAILED.\n", this,
497 // The URI load has been canceled by the observer. Clean up
501 mRequests
.Remove(request
);
506 mForegroundCount
-= 1;
511 // Ensure that we're part of our loadgroup while pending
512 if (foreground
&& mForegroundCount
== 1 && mLoadGroup
) {
513 mLoadGroup
->AddRequest(this, nullptr);
521 nsLoadGroup::RemoveRequest(nsIRequest
* request
, nsISupports
* ctxt
,
523 // Make sure we have a owning reference to the request we're about
525 nsCOMPtr
<nsIRequest
> kungFuDeathGrip(request
);
527 nsresult rv
= RemoveRequestFromHashtable(request
, aStatus
);
532 return NotifyRemovalObservers(request
, aStatus
);
535 nsresult
nsLoadGroup::RemoveRequestFromHashtable(nsIRequest
* request
,
537 NS_ENSURE_ARG_POINTER(request
);
540 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
541 nsAutoCString nameStr
;
542 request
->GetName(nameStr
);
543 LOG(("LOADGROUP [%p]: Removing request %p %s status %" PRIx32
545 this, request
, nameStr
.get(), static_cast<uint32_t>(aStatus
),
546 mRequests
.EntryCount() - 1));
550 // Remove the request from the group. If this fails, it means that
551 // the request was *not* in the group so do not update the foreground
552 // count or it will get messed up...
554 auto* entry
= static_cast<RequestMapEntry
*>(mRequests
.Search(request
));
557 LOG(("LOADGROUP [%p]: Unable to remove request %p. Not in group!\n", this,
560 return NS_ERROR_FAILURE
;
563 mRequests
.RemoveEntry(entry
);
565 // Cache the status of mDefaultLoadRequest, It'll be used later in
567 if (request
== mDefaultLoadRequest
) {
568 mDefaultStatus
= aStatus
;
571 // Collect telemetry stats only when default request is a timed channel.
572 // Don't include failed requests in the timing statistics.
573 if (mDefaultLoadIsTimed
&& NS_SUCCEEDED(aStatus
)) {
574 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(request
);
576 // Figure out if this request was served from the cache
579 rv
= timedChannel
->GetCacheReadStart(&timeStamp
);
580 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
584 if (request
== mDefaultLoadRequest
) {
585 TelemetryReportChannel(timedChannel
, true);
587 rv
= timedChannel
->GetAsyncOpen(&timeStamp
);
588 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
589 glean::http::subitem_open_latency_time
.AccumulateRawDuration(
590 timeStamp
- mDefaultRequestCreationTime
);
593 rv
= timedChannel
->GetResponseStart(&timeStamp
);
594 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
595 glean::http::subitem_first_byte_latency_time
.AccumulateRawDuration(
596 timeStamp
- mDefaultRequestCreationTime
);
599 TelemetryReportChannel(timedChannel
, false);
604 if (mRequests
.EntryCount() == 0) {
611 nsresult
nsLoadGroup::NotifyRemovalObservers(nsIRequest
* request
,
613 NS_ENSURE_ARG_POINTER(request
);
614 // Undo any group priority delta...
615 if (mPriority
!= 0) RescheduleRequest(request
, -mPriority
);
618 nsresult rv
= request
->GetLoadFlags(&flags
);
619 if (NS_FAILED(rv
)) return rv
;
621 bool foreground
= !(flags
& nsIRequest::LOAD_BACKGROUND
);
623 NS_ASSERTION(mForegroundCount
> 0, "ForegroundCount messed up");
624 mForegroundCount
-= 1;
627 if (foreground
|| mNotifyObserverAboutBackgroundRequests
) {
628 // Fire the OnStopRequest out to the observer...
629 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
632 ("LOADGROUP [%p]: Firing OnStopRequest for request %p."
633 "(foreground count=%d).\n",
634 this, request
, mForegroundCount
));
636 rv
= observer
->OnStopRequest(request
, aStatus
);
639 LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this,
644 // If that was the last request -> remove ourselves from loadgroup
645 if (foreground
&& mForegroundCount
== 0 && mLoadGroup
) {
646 mLoadGroup
->RemoveRequest(this, nullptr, aStatus
);
654 nsLoadGroup::GetRequests(nsISimpleEnumerator
** aRequests
) {
655 nsCOMArray
<nsIRequest
> requests
;
656 requests
.SetCapacity(mRequests
.EntryCount());
658 for (auto iter
= mRequests
.Iter(); !iter
.Done(); iter
.Next()) {
659 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
660 requests
.AppendObject(e
->mKey
);
663 return NS_NewArrayEnumerator(aRequests
, requests
, NS_GET_IID(nsIRequest
));
667 nsLoadGroup::GetTotalKeepAliveBytes(uint64_t* aTotalKeepAliveBytes
) {
668 MOZ_ASSERT(aTotalKeepAliveBytes
);
669 *aTotalKeepAliveBytes
= mPendingKeepaliveRequestSize
;
674 nsLoadGroup::SetTotalKeepAliveBytes(uint64_t aTotalKeepAliveBytes
) {
675 mPendingKeepaliveRequestSize
= aTotalKeepAliveBytes
;
680 nsLoadGroup::SetGroupObserver(nsIRequestObserver
* aObserver
) {
681 SetGroupObserver(aObserver
, false);
685 void nsLoadGroup::SetGroupObserver(nsIRequestObserver
* aObserver
,
686 bool aIncludeBackgroundRequests
) {
687 mObserver
= do_GetWeakReference(aObserver
);
688 mNotifyObserverAboutBackgroundRequests
= aIncludeBackgroundRequests
;
692 nsLoadGroup::GetGroupObserver(nsIRequestObserver
** aResult
) {
693 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
694 observer
.forget(aResult
);
699 nsLoadGroup::GetActiveCount(uint32_t* aResult
) {
700 *aResult
= mForegroundCount
;
705 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor
** aCallbacks
) {
706 NS_ENSURE_ARG_POINTER(aCallbacks
);
707 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
= mCallbacks
;
708 callbacks
.forget(aCallbacks
);
713 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
) {
714 mCallbacks
= aCallbacks
;
719 nsLoadGroup::GetRequestContextID(uint64_t* aRCID
) {
720 if (!mRequestContext
) {
721 return NS_ERROR_NOT_AVAILABLE
;
723 *aRCID
= mRequestContext
->GetID();
727 ////////////////////////////////////////////////////////////////////////////////
728 // nsILoadGroupChild methods:
731 nsLoadGroup::GetParentLoadGroup(nsILoadGroup
** aParentLoadGroup
) {
732 *aParentLoadGroup
= nullptr;
733 nsCOMPtr
<nsILoadGroup
> parent
= do_QueryReferent(mParentLoadGroup
);
734 if (!parent
) return NS_OK
;
735 parent
.forget(aParentLoadGroup
);
740 nsLoadGroup::SetParentLoadGroup(nsILoadGroup
* aParentLoadGroup
) {
741 mParentLoadGroup
= do_GetWeakReference(aParentLoadGroup
);
746 nsLoadGroup::GetChildLoadGroup(nsILoadGroup
** aChildLoadGroup
) {
747 *aChildLoadGroup
= do_AddRef(this).take();
752 nsLoadGroup::GetRootLoadGroup(nsILoadGroup
** aRootLoadGroup
) {
753 // first recursively try the root load group of our parent
754 nsCOMPtr
<nsILoadGroupChild
> ancestor
= do_QueryReferent(mParentLoadGroup
);
755 if (ancestor
) return ancestor
->GetRootLoadGroup(aRootLoadGroup
);
757 // next recursively try the root load group of our own load grop
758 ancestor
= do_QueryInterface(mLoadGroup
);
759 if (ancestor
) return ancestor
->GetRootLoadGroup(aRootLoadGroup
);
761 // finally just return this
762 *aRootLoadGroup
= do_AddRef(this).take();
766 ////////////////////////////////////////////////////////////////////////////////
767 // nsISupportsPriority methods:
770 nsLoadGroup::GetPriority(int32_t* aValue
) {
776 nsLoadGroup::SetPriority(int32_t aValue
) {
777 return AdjustPriority(aValue
- mPriority
);
781 nsLoadGroup::AdjustPriority(int32_t aDelta
) {
782 // Update the priority for each request that supports nsISupportsPriority
785 for (auto iter
= mRequests
.Iter(); !iter
.Done(); iter
.Next()) {
786 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
787 RescheduleRequest(e
->mKey
, aDelta
);
794 nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags
) {
795 *aFlags
= mDefaultLoadFlags
;
800 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags
) {
801 mDefaultLoadFlags
= aFlags
;
805 ////////////////////////////////////////////////////////////////////////////////
807 void nsLoadGroup::TelemetryReport() {
808 // We should only report HTTP_PAGE_* telemetry if the defaultRequest was
809 // actually successful.
810 if (mDefaultLoadIsTimed
&& NS_SUCCEEDED(mDefaultStatus
)) {
811 glean::http::request_per_page
.AccumulateSingleSample(mTimedRequests
);
812 if (mTimedRequests
) {
813 glean::http::request_per_page_from_cache
.AccumulateSingleSample(
814 mCachedRequests
* 100 / mTimedRequests
);
820 mDefaultLoadIsTimed
= false;
823 void nsLoadGroup::TelemetryReportChannel(nsITimedChannel
* aTimedChannel
,
824 bool aDefaultRequest
) {
828 rv
= aTimedChannel
->GetAsyncOpen(&asyncOpen
);
829 // We do not check !asyncOpen.IsNull() bellow, prevent ASSERTIONs this way
830 if (NS_FAILED(rv
) || asyncOpen
.IsNull()) return;
832 TimeStamp cacheReadStart
;
833 rv
= aTimedChannel
->GetCacheReadStart(&cacheReadStart
);
834 if (NS_FAILED(rv
)) return;
836 TimeStamp cacheReadEnd
;
837 rv
= aTimedChannel
->GetCacheReadEnd(&cacheReadEnd
);
838 if (NS_FAILED(rv
)) return;
840 TimeStamp domainLookupStart
;
841 rv
= aTimedChannel
->GetDomainLookupStart(&domainLookupStart
);
842 if (NS_FAILED(rv
)) return;
844 TimeStamp domainLookupEnd
;
845 rv
= aTimedChannel
->GetDomainLookupEnd(&domainLookupEnd
);
846 if (NS_FAILED(rv
)) return;
848 TimeStamp connectStart
;
849 rv
= aTimedChannel
->GetConnectStart(&connectStart
);
850 if (NS_FAILED(rv
)) return;
852 TimeStamp secureConnectionStart
;
853 rv
= aTimedChannel
->GetSecureConnectionStart(&secureConnectionStart
);
854 if (NS_FAILED(rv
)) return;
856 TimeStamp connectEnd
;
857 rv
= aTimedChannel
->GetConnectEnd(&connectEnd
);
858 if (NS_FAILED(rv
)) return;
860 TimeStamp requestStart
;
861 rv
= aTimedChannel
->GetRequestStart(&requestStart
);
862 if (NS_FAILED(rv
)) return;
864 TimeStamp responseStart
;
865 rv
= aTimedChannel
->GetResponseStart(&responseStart
);
866 if (NS_FAILED(rv
)) return;
868 TimeStamp responseEnd
;
869 rv
= aTimedChannel
->GetResponseEnd(&responseEnd
);
870 if (NS_FAILED(rv
)) return;
872 bool useHttp3
= false;
874 bool supportHttp3
= false;
875 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
=
876 do_QueryInterface(aTimedChannel
);
880 if (NS_SUCCEEDED(httpChannel
->GetResponseVersion(&major
, &minor
))) {
882 useHttp3
= major
== 3;
885 if (NS_FAILED(httpChannel
->GetSupportsHTTP3(&supportHttp3
))) {
886 supportHttp3
= false;
892 // Glean instrumentation of metrics previously collected via Geckoview
894 if (!domainLookupStart
.IsNull()) {
895 if (aDefaultRequest
) {
896 mozilla::glean::network::dns_start
.AccumulateRawDuration(
897 domainLookupStart
- asyncOpen
);
898 if (!domainLookupEnd
.IsNull()) {
899 mozilla::glean::network::dns_end
.AccumulateRawDuration(
900 domainLookupEnd
- domainLookupStart
);
905 mozilla::glean::network::sub_dns_start
.AccumulateRawDuration(
906 domainLookupStart
- asyncOpen
);
907 if (!domainLookupEnd
.IsNull()) {
908 mozilla::glean::network::sub_dns_end
.AccumulateRawDuration(
909 domainLookupEnd
- domainLookupStart
);
914 if (!connectEnd
.IsNull()) {
915 if (!connectStart
.IsNull()) {
916 if (aDefaultRequest
) {
917 mozilla::glean::network::tcp_connection
.AccumulateRawDuration(
918 connectEnd
- connectStart
);
922 mozilla::glean::network::sub_tcp_connection
.AccumulateRawDuration(
923 connectEnd
- connectStart
);
927 if (!secureConnectionStart
.IsNull()) {
928 if (aDefaultRequest
) {
929 mozilla::glean::network::tls_handshake
.AccumulateRawDuration(
930 connectEnd
- secureConnectionStart
);
934 mozilla::glean::network::sub_tls_handshake
.AccumulateRawDuration(
935 connectEnd
- secureConnectionStart
);
940 if (!requestStart
.IsNull() && !responseEnd
.IsNull()) {
941 if (aDefaultRequest
) {
942 mozilla::glean::network::open_to_first_sent
.AccumulateRawDuration(
943 requestStart
- asyncOpen
);
944 mozilla::glean::network::first_sent_to_last_received
945 .AccumulateRawDuration(responseEnd
- requestStart
);
947 if (cacheReadStart
.IsNull() && !responseStart
.IsNull()) {
948 mozilla::glean::network::open_to_first_received
.AccumulateRawDuration(
949 responseStart
- asyncOpen
);
954 mozilla::glean::network::sub_open_to_first_sent
.AccumulateRawDuration(
955 requestStart
- asyncOpen
);
956 mozilla::glean::network::sub_first_sent_to_last_received
957 .AccumulateRawDuration(responseEnd
- requestStart
);
958 if (cacheReadStart
.IsNull() && !responseStart
.IsNull()) {
959 mozilla::glean::network::sub_open_to_first_received
960 .AccumulateRawDuration(responseStart
- asyncOpen
);
965 if (!cacheReadStart
.IsNull() && !cacheReadEnd
.IsNull()) {
966 if (aDefaultRequest
) {
967 mozilla::glean::network::first_from_cache
.AccumulateRawDuration(
968 cacheReadStart
- asyncOpen
);
970 mozilla::glean::network::cache_read_time
.AccumulateRawDuration(
971 cacheReadEnd
- cacheReadStart
);
972 if (!requestStart
.IsNull() && !responseEnd
.IsNull()) {
973 mozilla::glean::network::http_revalidation
.AccumulateRawDuration(
974 responseEnd
- requestStart
);
980 mozilla::glean::network::sub_first_from_cache
.AccumulateRawDuration(
981 cacheReadStart
- asyncOpen
);
982 mozilla::glean::network::sub_cache_read_time
.AccumulateRawDuration(
983 cacheReadEnd
- cacheReadStart
);
984 if (!requestStart
.IsNull() && !responseEnd
.IsNull()) {
985 mozilla::glean::network::sub_http_revalidation
.AccumulateRawDuration(
986 responseEnd
- requestStart
);
992 if (!cacheReadEnd
.IsNull()) {
993 if (aDefaultRequest
) {
994 mozilla::glean::network::complete_load
.AccumulateRawDuration(
995 cacheReadEnd
- asyncOpen
);
996 mozilla::glean::network::complete_load_cached
.AccumulateRawDuration(
997 cacheReadEnd
- asyncOpen
);
999 mozilla::glean::network::sub_complete_load
.AccumulateRawDuration(
1000 cacheReadEnd
- asyncOpen
);
1001 mozilla::glean::network::sub_complete_load_cached
.AccumulateRawDuration(
1002 cacheReadEnd
- asyncOpen
);
1004 } else if (!responseEnd
.IsNull()) {
1005 if (aDefaultRequest
) {
1006 mozilla::glean::network::complete_load
.AccumulateRawDuration(responseEnd
-
1008 mozilla::glean::network::complete_load_net
.AccumulateRawDuration(
1009 responseEnd
- asyncOpen
);
1011 mozilla::glean::network::sub_complete_load
.AccumulateRawDuration(
1012 responseEnd
- asyncOpen
);
1013 mozilla::glean::network::sub_complete_load_net
.AccumulateRawDuration(
1014 responseEnd
- asyncOpen
);
1020 if ((useHttp3
|| supportHttp3
) && cacheReadStart
.IsNull() &&
1021 cacheReadEnd
.IsNull()) {
1022 nsCString key
= (useHttp3
) ? ((aDefaultRequest
) ? "uses_http3_page"_ns
1023 : "uses_http3_sub"_ns
)
1024 : ((aDefaultRequest
) ? "supports_http3_page"_ns
1025 : "supports_http3_sub"_ns
);
1027 if (!secureConnectionStart
.IsNull() && !connectEnd
.IsNull()) {
1028 mozilla::glean::network::http3_tls_handshake
.Get(key
)
1029 .AccumulateRawDuration(connectEnd
- secureConnectionStart
);
1032 if (supportHttp3
&& !connectStart
.IsNull() && !connectEnd
.IsNull()) {
1033 mozilla::glean::network::sup_http3_tcp_connection
.Get(key
)
1034 .AccumulateRawDuration(connectEnd
- connectStart
);
1037 if (!requestStart
.IsNull() && !responseEnd
.IsNull()) {
1038 mozilla::glean::network::http3_open_to_first_sent
.Get(key
)
1039 .AccumulateRawDuration(requestStart
- asyncOpen
);
1041 mozilla::glean::network::http3_first_sent_to_last_received
.Get(key
)
1042 .AccumulateRawDuration(responseEnd
- requestStart
);
1044 if (!responseStart
.IsNull()) {
1045 mozilla::glean::network::http3_open_to_first_received
.Get(key
)
1046 .AccumulateRawDuration(responseStart
- asyncOpen
);
1049 if (!responseEnd
.IsNull()) {
1050 mozilla::glean::network::http3_complete_load
.Get(key
)
1051 .AccumulateRawDuration(responseEnd
- asyncOpen
);
1057 bool hasHTTPSRR
= false;
1058 if (httpChannel
&& NS_SUCCEEDED(httpChannel
->GetHasHTTPSRR(&hasHTTPSRR
)) &&
1059 cacheReadStart
.IsNull() && cacheReadEnd
.IsNull() &&
1060 !requestStart
.IsNull()) {
1061 TimeDuration elapsed
= requestStart
- asyncOpen
;
1063 if (aDefaultRequest
) {
1064 glean::networking::http_channel_page_open_to_first_sent_https_rr
1065 .AccumulateRawDuration(elapsed
);
1067 glean::networking::http_channel_sub_open_to_first_sent_https_rr
1068 .AccumulateRawDuration(elapsed
);
1071 if (aDefaultRequest
) {
1072 glean::networking::http_channel_page_open_to_first_sent
1073 .AccumulateRawDuration(elapsed
);
1075 glean::networking::http_channel_sub_open_to_first_sent
1076 .AccumulateRawDuration(elapsed
);
1082 nsresult
nsLoadGroup::MergeLoadFlags(nsIRequest
* aRequest
,
1083 nsLoadFlags
& outFlags
) {
1085 nsLoadFlags flags
, oldFlags
;
1087 rv
= aRequest
->GetLoadFlags(&flags
);
1088 if (NS_FAILED(rv
)) {
1094 // Inherit some bits...
1095 flags
|= mLoadFlags
& kInheritedLoadFlags
;
1097 // ... and force the default flags.
1098 flags
|= mDefaultLoadFlags
;
1100 if (flags
!= oldFlags
) {
1101 rv
= aRequest
->SetLoadFlags(flags
);
1108 nsresult
nsLoadGroup::MergeDefaultLoadFlags(nsIRequest
* aRequest
,
1109 nsLoadFlags
& outFlags
) {
1111 nsLoadFlags flags
, oldFlags
;
1113 rv
= aRequest
->GetLoadFlags(&flags
);
1114 if (NS_FAILED(rv
)) {
1119 // ... and force the default flags.
1120 flags
|= mDefaultLoadFlags
;
1122 if (flags
!= oldFlags
) {
1123 rv
= aRequest
->SetLoadFlags(flags
);
1129 nsresult
nsLoadGroup::Init() {
1130 mRequestContextService
= RequestContextService::GetOrCreate();
1131 if (mRequestContextService
) {
1132 Unused
<< mRequestContextService
->NewRequestContext(
1133 getter_AddRefs(mRequestContext
));
1136 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1137 NS_ENSURE_STATE(os
);
1139 Unused
<< os
->AddObserver(this, "last-pb-context-exited", true);
1144 nsresult
nsLoadGroup::InitWithRequestContextId(
1145 const uint64_t& aRequestContextId
) {
1146 mRequestContextService
= RequestContextService::GetOrCreate();
1147 if (mRequestContextService
) {
1148 Unused
<< mRequestContextService
->GetRequestContext(
1149 aRequestContextId
, getter_AddRefs(mRequestContext
));
1151 mExternalRequestContext
= true;
1153 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1154 NS_ENSURE_STATE(os
);
1156 Unused
<< os
->AddObserver(this, "last-pb-context-exited", true);
1162 nsLoadGroup::Observe(nsISupports
* aSubject
, const char* aTopic
,
1163 const char16_t
* aData
) {
1164 MOZ_ASSERT(!strcmp(aTopic
, "last-pb-context-exited"));
1166 OriginAttributes attrs
;
1167 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs
);
1168 if (!attrs
.IsPrivateBrowsing()) {
1172 mBrowsingContextDiscarded
= true;
1177 nsLoadGroup::GetIsBrowsingContextDiscarded(bool* aIsBrowsingContextDiscarded
) {
1178 *aIsBrowsingContextDiscarded
= mBrowsingContextDiscarded
;
1183 } // namespace mozilla