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 "mozilla/Telemetry.h"
19 #include "nsIHttpChannelInternal.h"
20 #include "nsITimedChannel.h"
21 #include "nsIInterfaceRequestor.h"
22 #include "nsIRequestObserver.h"
23 #include "CacheObserver.h"
24 #include "MainThreadUtils.h"
25 #include "RequestContextService.h"
26 #include "mozilla/StoragePrincipalHelper.h"
27 #include "mozilla/Unused.h"
33 // Log module for nsILoadGroup logging...
35 // To enable logging (see prlog.h for full details):
37 // set MOZ_LOG=LoadGroup:5
38 // set MOZ_LOG_FILE=network.log
40 // This enables LogLevel::Debug level information and places all output in
41 // the file network.log.
43 static LazyLogModule
gLoadGroupLog("LoadGroup");
45 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
47 ////////////////////////////////////////////////////////////////////////////////
49 class RequestMapEntry
: public PLDHashEntryHdr
{
51 explicit RequestMapEntry(nsIRequest
* aRequest
) : mKey(aRequest
) {}
53 nsCOMPtr
<nsIRequest
> mKey
;
56 static bool RequestHashMatchEntry(const PLDHashEntryHdr
* entry
,
58 const RequestMapEntry
* e
= static_cast<const RequestMapEntry
*>(entry
);
59 const nsIRequest
* request
= static_cast<const nsIRequest
*>(key
);
61 return e
->mKey
== request
;
64 static void RequestHashClearEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
) {
65 RequestMapEntry
* e
= static_cast<RequestMapEntry
*>(entry
);
67 // An entry is being cleared, let the entry do its own cleanup.
68 e
->~RequestMapEntry();
71 static void RequestHashInitEntry(PLDHashEntryHdr
* entry
, const void* key
) {
72 const nsIRequest
* const_request
= static_cast<const nsIRequest
*>(key
);
73 nsIRequest
* request
= const_cast<nsIRequest
*>(const_request
);
75 // Initialize the entry with placement new
76 new (entry
) RequestMapEntry(request
);
79 static const PLDHashTableOps sRequestHashOps
= {
80 PLDHashTable::HashVoidPtrKeyStub
, RequestHashMatchEntry
,
81 PLDHashTable::MoveEntryStub
, RequestHashClearEntry
, RequestHashInitEntry
};
83 static void RescheduleRequest(nsIRequest
* aRequest
, int32_t delta
) {
84 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(aRequest
);
85 if (p
) p
->AdjustPriority(delta
);
88 nsLoadGroup::nsLoadGroup()
89 : mRequests(&sRequestHashOps
, sizeof(RequestMapEntry
)) {
90 LOG(("LOADGROUP [%p]: Created.\n", this));
93 nsLoadGroup::~nsLoadGroup() {
94 DebugOnly
<nsresult
> rv
=
95 CancelWithReason(NS_BINDING_ABORTED
, "nsLoadGroup::~nsLoadGroup"_ns
);
96 NS_ASSERTION(NS_SUCCEEDED(rv
), "Cancel failed");
98 mDefaultLoadRequest
= nullptr;
100 if (mRequestContext
&& !mExternalRequestContext
) {
101 mRequestContextService
->RemoveRequestContext(mRequestContext
->GetID());
102 if (IsNeckoChild() && gNeckoChild
) {
103 gNeckoChild
->SendRemoveRequestContext(mRequestContext
->GetID());
107 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
109 Unused
<< os
->RemoveObserver(this, "last-pb-context-exited");
112 LOG(("LOADGROUP [%p]: Destroyed.\n", this));
115 ////////////////////////////////////////////////////////////////////////////////
116 // nsISupports methods:
118 NS_IMPL_ISUPPORTS(nsLoadGroup
, nsILoadGroup
, nsILoadGroupChild
, nsIRequest
,
119 nsISupportsPriority
, nsISupportsWeakReference
, nsIObserver
)
121 ////////////////////////////////////////////////////////////////////////////////
122 // nsIRequest methods:
125 nsLoadGroup::GetName(nsACString
& result
) {
126 // XXX is this the right "name" for a load group?
128 if (!mDefaultLoadRequest
) {
133 return mDefaultLoadRequest
->GetName(result
);
137 nsLoadGroup::IsPending(bool* aResult
) {
138 *aResult
= mForegroundCount
> 0;
143 nsLoadGroup::GetStatus(nsresult
* status
) {
144 if (NS_SUCCEEDED(mStatus
) && mDefaultLoadRequest
) {
145 return mDefaultLoadRequest
->GetStatus(status
);
152 static bool AppendRequestsToArray(PLDHashTable
* aTable
,
153 nsTArray
<nsIRequest
*>* aArray
) {
154 for (auto iter
= aTable
->Iter(); !iter
.Done(); iter
.Next()) {
155 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
156 nsIRequest
* request
= e
->mKey
;
157 MOZ_DIAGNOSTIC_ASSERT(request
, "Null key in mRequests PLDHashTable entry");
159 // XXX(Bug 1631371) Check if this should use a fallible operation as it
160 // pretended earlier.
161 aArray
->AppendElement(request
);
165 if (aArray
->Length() != aTable
->EntryCount()) {
166 for (uint32_t i
= 0, len
= aArray
->Length(); i
< len
; ++i
) {
167 NS_RELEASE((*aArray
)[i
]);
174 NS_IMETHODIMP
nsLoadGroup::SetCanceledReason(const nsACString
& aReason
) {
175 return SetCanceledReasonImpl(aReason
);
178 NS_IMETHODIMP
nsLoadGroup::GetCanceledReason(nsACString
& aReason
) {
179 return GetCanceledReasonImpl(aReason
);
182 NS_IMETHODIMP
nsLoadGroup::CancelWithReason(nsresult aStatus
,
183 const nsACString
& aReason
) {
184 return CancelWithReasonImpl(aStatus
, aReason
);
188 nsLoadGroup::Cancel(nsresult status
) {
189 MOZ_ASSERT(NS_IsMainThread());
191 NS_ASSERTION(NS_FAILED(status
), "shouldn't cancel with a success code");
193 uint32_t count
= mRequests
.EntryCount();
195 AutoTArray
<nsIRequest
*, 8> requests
;
197 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
198 return NS_ERROR_OUT_OF_MEMORY
;
201 // set the load group status to our cancel status while we cancel
202 // all our requests...once the cancel is done, we'll reset it...
206 // Set the flag indicating that the loadgroup is being canceled... This
207 // prevents any new channels from being added during the operation.
211 nsresult firstError
= NS_OK
;
213 nsCOMPtr
<nsIRequest
> request
= requests
.ElementAt(--count
);
215 NS_ASSERTION(request
, "NULL request found in list.");
217 if (!mRequests
.Search(request
)) {
218 // |request| was removed already
219 // We need to null out the entry in the request array so we don't try
220 // to notify the observers for this request.
221 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(count
));
222 requests
.ElementAt(count
) = nullptr;
227 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
228 nsAutoCString nameStr
;
229 request
->GetName(nameStr
);
230 LOG(("LOADGROUP [%p]: Canceling request %p %s.\n", this, request
.get(),
234 // Cancel the request...
235 rv
= request
->CancelWithReason(status
, mCanceledReason
);
237 // Remember the first failure and return it...
238 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
240 if (NS_FAILED(RemoveRequestFromHashtable(request
, status
))) {
241 // It's possible that request->Cancel causes the request to be removed
242 // from the loadgroup causing RemoveRequestFromHashtable to fail.
243 // In that case we shouldn't call NotifyRemovalObservers or decrement
244 // mForegroundCount since that has already happened.
245 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(count
));
246 requests
.ElementAt(count
) = nullptr;
252 for (count
= requests
.Length(); count
> 0;) {
253 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
254 (void)NotifyRemovalObservers(request
, status
);
257 if (mRequestContext
) {
258 Unused
<< mRequestContext
->CancelTailPendingRequests(status
);
262 NS_ASSERTION(mRequests
.EntryCount() == 0, "Request list is not empty.");
263 NS_ASSERTION(mForegroundCount
== 0, "Foreground URLs are active.");
267 mIsCanceling
= false;
273 nsLoadGroup::Suspend() {
274 nsresult rv
, firstError
;
275 uint32_t count
= mRequests
.EntryCount();
277 AutoTArray
<nsIRequest
*, 8> requests
;
279 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
280 return NS_ERROR_OUT_OF_MEMORY
;
285 // Operate the elements from back to front so that if items get
286 // get removed from the list it won't affect our iteration
289 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
291 NS_ASSERTION(request
, "NULL request found in list.");
292 if (!request
) continue;
294 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
295 nsAutoCString nameStr
;
296 request
->GetName(nameStr
);
297 LOG(("LOADGROUP [%p]: Suspending request %p %s.\n", this, request
.get(),
301 // Suspend the request...
302 rv
= request
->Suspend();
304 // Remember the first failure and return it...
305 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
312 nsLoadGroup::Resume() {
313 nsresult rv
, firstError
;
314 uint32_t count
= mRequests
.EntryCount();
316 AutoTArray
<nsIRequest
*, 8> requests
;
318 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
319 return NS_ERROR_OUT_OF_MEMORY
;
324 // Operate the elements from back to front so that if items get
325 // get removed from the list it won't affect our iteration
328 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
330 NS_ASSERTION(request
, "NULL request found in list.");
331 if (!request
) continue;
333 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
334 nsAutoCString nameStr
;
335 request
->GetName(nameStr
);
336 LOG(("LOADGROUP [%p]: Resuming request %p %s.\n", this, request
.get(),
340 // Resume the request...
341 rv
= request
->Resume();
343 // Remember the first failure and return it...
344 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
351 nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags
) {
352 *aLoadFlags
= mLoadFlags
;
357 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags
) {
358 mLoadFlags
= aLoadFlags
;
363 nsLoadGroup::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
364 return GetTRRModeImpl(aTRRMode
);
368 nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
369 return SetTRRModeImpl(aTRRMode
);
373 nsLoadGroup::GetLoadGroup(nsILoadGroup
** loadGroup
) {
374 nsCOMPtr
<nsILoadGroup
> result
= mLoadGroup
;
375 result
.forget(loadGroup
);
380 nsLoadGroup::SetLoadGroup(nsILoadGroup
* loadGroup
) {
381 mLoadGroup
= loadGroup
;
385 ////////////////////////////////////////////////////////////////////////////////
386 // nsILoadGroup methods:
389 nsLoadGroup::GetDefaultLoadRequest(nsIRequest
** aRequest
) {
390 nsCOMPtr
<nsIRequest
> result
= mDefaultLoadRequest
;
391 result
.forget(aRequest
);
396 nsLoadGroup::SetDefaultLoadRequest(nsIRequest
* aRequest
) {
397 LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this,
400 mDefaultLoadRequest
= aRequest
;
401 // Inherit the group load flags from the default load request
402 if (mDefaultLoadRequest
) {
403 mDefaultLoadRequest
->GetLoadFlags(&mLoadFlags
);
405 // Mask off any bits that are not part of the nsIRequest flags.
406 // in particular, nsIChannel::LOAD_DOCUMENT_URI...
408 mLoadFlags
&= nsIRequest::LOAD_REQUESTMASK
;
410 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(aRequest
);
411 mDefaultLoadIsTimed
= timedChannel
!= nullptr;
412 if (mDefaultLoadIsTimed
) {
413 timedChannel
->GetChannelCreation(&mDefaultRequestCreationTime
);
414 timedChannel
->SetTimingEnabled(true);
417 // Else, do not change the group's load flags (see bug 95981)
422 nsLoadGroup::AddRequest(nsIRequest
* request
, nsISupports
* ctxt
) {
425 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
426 nsAutoCString nameStr
;
427 request
->GetName(nameStr
);
428 LOG(("LOADGROUP [%p]: Adding request %p %s (count=%d).\n", this, request
,
429 nameStr
.get(), mRequests
.EntryCount()));
432 NS_ASSERTION(!mRequests
.Search(request
),
433 "Entry added to loadgroup twice, don't do that");
436 // Do not add the channel, if the loadgroup is being canceled...
440 ("LOADGROUP [%p]: AddChannel() ABORTED because LoadGroup is"
441 " being canceled!!\n",
444 return NS_BINDING_ABORTED
;
448 // if the request is the default load request or if the default load
449 // request is null, then the load group should inherit its load flags from
450 // the request, but also we need to enforce defaultLoadFlags.
451 if (mDefaultLoadRequest
== request
|| !mDefaultLoadRequest
) {
452 rv
= MergeDefaultLoadFlags(request
, flags
);
454 rv
= MergeLoadFlags(request
, flags
);
456 if (NS_FAILED(rv
)) return rv
;
459 // Add the request to the list of active requests...
462 auto* entry
= static_cast<RequestMapEntry
*>(mRequests
.Add(request
, fallible
));
464 return NS_ERROR_OUT_OF_MEMORY
;
467 if (mPriority
!= 0) RescheduleRequest(request
, mPriority
);
469 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(request
);
470 if (timedChannel
) timedChannel
->SetTimingEnabled(true);
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 // Collect telemetry stats only when default request is a timed channel.
566 // Don't include failed requests in the timing statistics.
567 if (mDefaultLoadIsTimed
&& NS_SUCCEEDED(aStatus
)) {
568 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(request
);
570 // Figure out if this request was served from the cache
573 rv
= timedChannel
->GetCacheReadStart(&timeStamp
);
574 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
578 rv
= timedChannel
->GetAsyncOpen(&timeStamp
);
579 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
580 Telemetry::AccumulateTimeDelta(
581 Telemetry::HTTP_SUBITEM_OPEN_LATENCY_TIME
,
582 mDefaultRequestCreationTime
, timeStamp
);
585 rv
= timedChannel
->GetResponseStart(&timeStamp
);
586 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
587 Telemetry::AccumulateTimeDelta(
588 Telemetry::HTTP_SUBITEM_FIRST_BYTE_LATENCY_TIME
,
589 mDefaultRequestCreationTime
, timeStamp
);
592 TelemetryReportChannel(timedChannel
, false);
596 if (mRequests
.EntryCount() == 0) {
603 nsresult
nsLoadGroup::NotifyRemovalObservers(nsIRequest
* request
,
605 NS_ENSURE_ARG_POINTER(request
);
606 // Undo any group priority delta...
607 if (mPriority
!= 0) RescheduleRequest(request
, -mPriority
);
610 nsresult rv
= request
->GetLoadFlags(&flags
);
611 if (NS_FAILED(rv
)) return rv
;
613 bool foreground
= !(flags
& nsIRequest::LOAD_BACKGROUND
);
615 NS_ASSERTION(mForegroundCount
> 0, "ForegroundCount messed up");
616 mForegroundCount
-= 1;
619 if (foreground
|| mNotifyObserverAboutBackgroundRequests
) {
620 // Fire the OnStopRequest out to the observer...
621 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
624 ("LOADGROUP [%p]: Firing OnStopRequest for request %p."
625 "(foreground count=%d).\n",
626 this, request
, mForegroundCount
));
628 rv
= observer
->OnStopRequest(request
, aStatus
);
631 LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this,
636 // If that was the last request -> remove ourselves from loadgroup
637 if (foreground
&& mForegroundCount
== 0 && mLoadGroup
) {
638 mLoadGroup
->RemoveRequest(this, nullptr, aStatus
);
646 nsLoadGroup::GetRequests(nsISimpleEnumerator
** aRequests
) {
647 nsCOMArray
<nsIRequest
> requests
;
648 requests
.SetCapacity(mRequests
.EntryCount());
650 for (auto iter
= mRequests
.Iter(); !iter
.Done(); iter
.Next()) {
651 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
652 requests
.AppendObject(e
->mKey
);
655 return NS_NewArrayEnumerator(aRequests
, requests
, NS_GET_IID(nsIRequest
));
659 nsLoadGroup::SetGroupObserver(nsIRequestObserver
* aObserver
) {
660 SetGroupObserver(aObserver
, false);
664 void nsLoadGroup::SetGroupObserver(nsIRequestObserver
* aObserver
,
665 bool aIncludeBackgroundRequests
) {
666 mObserver
= do_GetWeakReference(aObserver
);
667 mNotifyObserverAboutBackgroundRequests
= aIncludeBackgroundRequests
;
671 nsLoadGroup::GetGroupObserver(nsIRequestObserver
** aResult
) {
672 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
673 observer
.forget(aResult
);
678 nsLoadGroup::GetActiveCount(uint32_t* aResult
) {
679 *aResult
= mForegroundCount
;
684 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor
** aCallbacks
) {
685 NS_ENSURE_ARG_POINTER(aCallbacks
);
686 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
= mCallbacks
;
687 callbacks
.forget(aCallbacks
);
692 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
) {
693 mCallbacks
= aCallbacks
;
698 nsLoadGroup::GetRequestContextID(uint64_t* aRCID
) {
699 if (!mRequestContext
) {
700 return NS_ERROR_NOT_AVAILABLE
;
702 *aRCID
= mRequestContext
->GetID();
706 ////////////////////////////////////////////////////////////////////////////////
707 // nsILoadGroupChild methods:
710 nsLoadGroup::GetParentLoadGroup(nsILoadGroup
** aParentLoadGroup
) {
711 *aParentLoadGroup
= nullptr;
712 nsCOMPtr
<nsILoadGroup
> parent
= do_QueryReferent(mParentLoadGroup
);
713 if (!parent
) return NS_OK
;
714 parent
.forget(aParentLoadGroup
);
719 nsLoadGroup::SetParentLoadGroup(nsILoadGroup
* aParentLoadGroup
) {
720 mParentLoadGroup
= do_GetWeakReference(aParentLoadGroup
);
725 nsLoadGroup::GetChildLoadGroup(nsILoadGroup
** aChildLoadGroup
) {
726 *aChildLoadGroup
= do_AddRef(this).take();
731 nsLoadGroup::GetRootLoadGroup(nsILoadGroup
** aRootLoadGroup
) {
732 // first recursively try the root load group of our parent
733 nsCOMPtr
<nsILoadGroupChild
> ancestor
= do_QueryReferent(mParentLoadGroup
);
734 if (ancestor
) return ancestor
->GetRootLoadGroup(aRootLoadGroup
);
736 // next recursively try the root load group of our own load grop
737 ancestor
= do_QueryInterface(mLoadGroup
);
738 if (ancestor
) return ancestor
->GetRootLoadGroup(aRootLoadGroup
);
740 // finally just return this
741 *aRootLoadGroup
= do_AddRef(this).take();
745 ////////////////////////////////////////////////////////////////////////////////
746 // nsISupportsPriority methods:
749 nsLoadGroup::GetPriority(int32_t* aValue
) {
755 nsLoadGroup::SetPriority(int32_t aValue
) {
756 return AdjustPriority(aValue
- mPriority
);
760 nsLoadGroup::AdjustPriority(int32_t aDelta
) {
761 // Update the priority for each request that supports nsISupportsPriority
764 for (auto iter
= mRequests
.Iter(); !iter
.Done(); iter
.Next()) {
765 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
766 RescheduleRequest(e
->mKey
, aDelta
);
773 nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags
) {
774 *aFlags
= mDefaultLoadFlags
;
779 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags
) {
780 mDefaultLoadFlags
= aFlags
;
784 ////////////////////////////////////////////////////////////////////////////////
786 void nsLoadGroup::TelemetryReport() {
787 nsresult defaultStatus
= NS_ERROR_INVALID_ARG
;
788 // We should only report HTTP_PAGE_* telemetry if the defaultRequest was
789 // actually successful.
790 if (mDefaultLoadRequest
) {
791 mDefaultLoadRequest
->GetStatus(&defaultStatus
);
793 if (mDefaultLoadIsTimed
&& NS_SUCCEEDED(defaultStatus
)) {
794 Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE
, mTimedRequests
);
795 if (mTimedRequests
) {
796 Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE_FROM_CACHE
,
797 mCachedRequests
* 100 / mTimedRequests
);
800 nsCOMPtr
<nsITimedChannel
> timedChannel
=
801 do_QueryInterface(mDefaultLoadRequest
);
802 if (timedChannel
) TelemetryReportChannel(timedChannel
, true);
807 mDefaultLoadIsTimed
= false;
810 void nsLoadGroup::TelemetryReportChannel(nsITimedChannel
* aTimedChannel
,
811 bool aDefaultRequest
) {
814 rv
= aTimedChannel
->GetTimingEnabled(&timingEnabled
);
815 if (NS_FAILED(rv
) || !timingEnabled
) return;
818 rv
= aTimedChannel
->GetAsyncOpen(&asyncOpen
);
819 // We do not check !asyncOpen.IsNull() bellow, prevent ASSERTIONs this way
820 if (NS_FAILED(rv
) || asyncOpen
.IsNull()) return;
822 TimeStamp cacheReadStart
;
823 rv
= aTimedChannel
->GetCacheReadStart(&cacheReadStart
);
824 if (NS_FAILED(rv
)) return;
826 TimeStamp cacheReadEnd
;
827 rv
= aTimedChannel
->GetCacheReadEnd(&cacheReadEnd
);
828 if (NS_FAILED(rv
)) return;
830 TimeStamp domainLookupStart
;
831 rv
= aTimedChannel
->GetDomainLookupStart(&domainLookupStart
);
832 if (NS_FAILED(rv
)) return;
834 TimeStamp domainLookupEnd
;
835 rv
= aTimedChannel
->GetDomainLookupEnd(&domainLookupEnd
);
836 if (NS_FAILED(rv
)) return;
838 TimeStamp connectStart
;
839 rv
= aTimedChannel
->GetConnectStart(&connectStart
);
840 if (NS_FAILED(rv
)) return;
842 TimeStamp secureConnectionStart
;
843 rv
= aTimedChannel
->GetSecureConnectionStart(&secureConnectionStart
);
844 if (NS_FAILED(rv
)) return;
846 TimeStamp connectEnd
;
847 rv
= aTimedChannel
->GetConnectEnd(&connectEnd
);
848 if (NS_FAILED(rv
)) return;
850 TimeStamp requestStart
;
851 rv
= aTimedChannel
->GetRequestStart(&requestStart
);
852 if (NS_FAILED(rv
)) return;
854 TimeStamp responseStart
;
855 rv
= aTimedChannel
->GetResponseStart(&responseStart
);
856 if (NS_FAILED(rv
)) return;
858 TimeStamp responseEnd
;
859 rv
= aTimedChannel
->GetResponseEnd(&responseEnd
);
860 if (NS_FAILED(rv
)) return;
862 bool useHttp3
= false;
863 bool supportHttp3
= false;
864 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
=
865 do_QueryInterface(aTimedChannel
);
869 if (NS_SUCCEEDED(httpChannel
->GetResponseVersion(&major
, &minor
))) {
870 useHttp3
= major
== 3;
872 if (NS_FAILED(httpChannel
->GetSupportsHTTP3(&supportHttp3
))) {
873 supportHttp3
= false;
879 #define HTTP_REQUEST_HISTOGRAMS(prefix) \
880 if (!domainLookupStart.IsNull()) { \
881 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_DNS_ISSUE_TIME, \
882 asyncOpen, domainLookupStart); \
885 if (!domainLookupStart.IsNull() && !domainLookupEnd.IsNull()) { \
886 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_DNS_LOOKUP_TIME, \
887 domainLookupStart, domainLookupEnd); \
890 if (!secureConnectionStart.IsNull() && !connectEnd.IsNull()) { \
891 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_TLS_HANDSHAKE, \
892 secureConnectionStart, connectEnd); \
895 if (!connectStart.IsNull() && !connectEnd.IsNull()) { \
896 Telemetry::AccumulateTimeDelta( \
897 Telemetry::HTTP_##prefix##_TCP_CONNECTION_2, connectStart, \
901 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
902 Telemetry::AccumulateTimeDelta( \
903 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_SENT, asyncOpen, \
906 Telemetry::AccumulateTimeDelta( \
907 Telemetry::HTTP_##prefix##_FIRST_SENT_TO_LAST_RECEIVED, requestStart, \
910 if (cacheReadStart.IsNull() && !responseStart.IsNull()) { \
911 Telemetry::AccumulateTimeDelta( \
912 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_RECEIVED, asyncOpen, \
917 if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) { \
918 Telemetry::AccumulateTimeDelta( \
919 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_FROM_CACHE_V2, asyncOpen, \
922 Telemetry::AccumulateTimeDelta( \
923 Telemetry::HTTP_##prefix##_CACHE_READ_TIME_V2, cacheReadStart, \
926 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
927 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_REVALIDATION, \
928 requestStart, responseEnd); \
932 if (!cacheReadEnd.IsNull()) { \
933 Telemetry::AccumulateTimeDelta( \
934 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_V2, asyncOpen, cacheReadEnd); \
935 Telemetry::AccumulateTimeDelta( \
936 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_CACHED_V2, asyncOpen, \
938 } else if (!responseEnd.IsNull()) { \
939 Telemetry::AccumulateTimeDelta( \
940 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_V2, asyncOpen, responseEnd); \
941 Telemetry::AccumulateTimeDelta( \
942 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_NET_V2, asyncOpen, \
946 if (aDefaultRequest
) {
947 HTTP_REQUEST_HISTOGRAMS(PAGE
)
949 HTTP_REQUEST_HISTOGRAMS(SUB
)
952 if ((useHttp3
|| supportHttp3
) && cacheReadStart
.IsNull() &&
953 cacheReadEnd
.IsNull()) {
954 nsCString key
= (useHttp3
) ? ((aDefaultRequest
) ? "uses_http3_page"_ns
955 : "uses_http3_sub"_ns
)
956 : ((aDefaultRequest
) ? "supports_http3_page"_ns
957 : "supports_http3_sub"_ns
);
959 if (!secureConnectionStart
.IsNull() && !connectEnd
.IsNull()) {
960 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_TLS_HANDSHAKE
, key
,
961 secureConnectionStart
, connectEnd
);
964 if (supportHttp3
&& !connectStart
.IsNull() && !connectEnd
.IsNull()) {
965 Telemetry::AccumulateTimeDelta(Telemetry::SUP_HTTP3_TCP_CONNECTION
, key
,
966 connectStart
, connectEnd
);
969 if (!requestStart
.IsNull() && !responseEnd
.IsNull()) {
970 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_OPEN_TO_FIRST_SENT
, key
,
971 asyncOpen
, requestStart
);
973 Telemetry::AccumulateTimeDelta(
974 Telemetry::HTTP3_FIRST_SENT_TO_LAST_RECEIVED
, key
, requestStart
,
977 if (!responseStart
.IsNull()) {
978 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_OPEN_TO_FIRST_RECEIVED
,
979 key
, asyncOpen
, responseStart
);
982 if (!responseEnd
.IsNull()) {
983 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_COMPLETE_LOAD
, key
,
984 asyncOpen
, responseEnd
);
989 bool hasHTTPSRR
= false;
990 if (httpChannel
&& NS_SUCCEEDED(httpChannel
->GetHasHTTPSRR(&hasHTTPSRR
)) &&
991 cacheReadStart
.IsNull() && cacheReadEnd
.IsNull() &&
992 !requestStart
.IsNull()) {
993 nsCString key
= (hasHTTPSRR
) ? ((aDefaultRequest
) ? "uses_https_rr_page"_ns
994 : "uses_https_rr_sub"_ns
)
995 : ((aDefaultRequest
) ? "no_https_rr_page"_ns
996 : "no_https_rr_sub"_ns
);
997 Telemetry::AccumulateTimeDelta(Telemetry::HTTPS_RR_OPEN_TO_FIRST_SENT
, key
,
998 asyncOpen
, requestStart
);
1001 if (StaticPrefs::network_trr_odoh_enabled() && !domainLookupStart
.IsNull() &&
1002 !domainLookupEnd
.IsNull()) {
1003 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
1004 bool ODoHActivated
= false;
1005 if (dns
&& NS_SUCCEEDED(dns
->GetODoHActivated(&ODoHActivated
)) &&
1007 if (aDefaultRequest
) {
1008 Telemetry::AccumulateTimeDelta(
1009 Telemetry::HTTP_PAGE_DNS_ODOH_LOOKUP_TIME
, domainLookupStart
,
1012 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_SUB_DNS_ODOH_LOOKUP_TIME
,
1013 domainLookupStart
, domainLookupEnd
);
1018 #undef HTTP_REQUEST_HISTOGRAMS
1021 nsresult
nsLoadGroup::MergeLoadFlags(nsIRequest
* aRequest
,
1022 nsLoadFlags
& outFlags
) {
1024 nsLoadFlags flags
, oldFlags
;
1026 rv
= aRequest
->GetLoadFlags(&flags
);
1027 if (NS_FAILED(rv
)) {
1033 // Inherit the following bits...
1034 flags
|= (mLoadFlags
&
1035 (LOAD_BACKGROUND
| LOAD_BYPASS_CACHE
| LOAD_FROM_CACHE
|
1036 VALIDATE_ALWAYS
| VALIDATE_ONCE_PER_SESSION
| VALIDATE_NEVER
));
1038 // ... and force the default flags.
1039 flags
|= mDefaultLoadFlags
;
1041 if (flags
!= oldFlags
) {
1042 rv
= aRequest
->SetLoadFlags(flags
);
1049 nsresult
nsLoadGroup::MergeDefaultLoadFlags(nsIRequest
* aRequest
,
1050 nsLoadFlags
& outFlags
) {
1052 nsLoadFlags flags
, oldFlags
;
1054 rv
= aRequest
->GetLoadFlags(&flags
);
1055 if (NS_FAILED(rv
)) {
1060 // ... and force the default flags.
1061 flags
|= mDefaultLoadFlags
;
1063 if (flags
!= oldFlags
) {
1064 rv
= aRequest
->SetLoadFlags(flags
);
1070 nsresult
nsLoadGroup::Init() {
1071 mRequestContextService
= RequestContextService::GetOrCreate();
1072 if (mRequestContextService
) {
1073 Unused
<< mRequestContextService
->NewRequestContext(
1074 getter_AddRefs(mRequestContext
));
1077 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1078 NS_ENSURE_STATE(os
);
1080 Unused
<< os
->AddObserver(this, "last-pb-context-exited", true);
1085 nsresult
nsLoadGroup::InitWithRequestContextId(
1086 const uint64_t& aRequestContextId
) {
1087 mRequestContextService
= RequestContextService::GetOrCreate();
1088 if (mRequestContextService
) {
1089 Unused
<< mRequestContextService
->GetRequestContext(
1090 aRequestContextId
, getter_AddRefs(mRequestContext
));
1092 mExternalRequestContext
= true;
1094 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1095 NS_ENSURE_STATE(os
);
1097 Unused
<< os
->AddObserver(this, "last-pb-context-exited", true);
1103 nsLoadGroup::Observe(nsISupports
* aSubject
, const char* aTopic
,
1104 const char16_t
* aData
) {
1105 MOZ_ASSERT(!strcmp(aTopic
, "last-pb-context-exited"));
1107 OriginAttributes attrs
;
1108 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs
);
1109 if (attrs
.mPrivateBrowsingId
== 0) {
1113 mBrowsingContextDiscarded
= true;
1118 nsLoadGroup::GetIsBrowsingContextDiscarded(bool* aIsBrowsingContextDiscarded
) {
1119 *aIsBrowsingContextDiscarded
= mBrowsingContextDiscarded
;
1124 } // namespace mozilla