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"
28 #include "mozilla/net/NeckoCommon.h"
29 #include "mozilla/net/NeckoChild.h"
30 #include "mozilla/StaticPrefs_network.h"
36 // Log module for nsILoadGroup logging...
38 // To enable logging (see prlog.h for full details):
40 // set MOZ_LOG=LoadGroup:5
41 // set MOZ_LOG_FILE=network.log
43 // This enables LogLevel::Debug level information and places all output in
44 // the file network.log.
46 static LazyLogModule
gLoadGroupLog("LoadGroup");
48 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
50 ////////////////////////////////////////////////////////////////////////////////
52 class RequestMapEntry
: public PLDHashEntryHdr
{
54 explicit RequestMapEntry(nsIRequest
* aRequest
) : mKey(aRequest
) {}
56 nsCOMPtr
<nsIRequest
> mKey
;
59 static bool RequestHashMatchEntry(const PLDHashEntryHdr
* entry
,
61 const RequestMapEntry
* e
= static_cast<const RequestMapEntry
*>(entry
);
62 const nsIRequest
* request
= static_cast<const nsIRequest
*>(key
);
64 return e
->mKey
== request
;
67 static void RequestHashClearEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
) {
68 RequestMapEntry
* e
= static_cast<RequestMapEntry
*>(entry
);
70 // An entry is being cleared, let the entry do its own cleanup.
71 e
->~RequestMapEntry();
74 static void RequestHashInitEntry(PLDHashEntryHdr
* entry
, const void* key
) {
75 const nsIRequest
* const_request
= static_cast<const nsIRequest
*>(key
);
76 nsIRequest
* request
= const_cast<nsIRequest
*>(const_request
);
78 // Initialize the entry with placement new
79 new (entry
) RequestMapEntry(request
);
82 static const PLDHashTableOps sRequestHashOps
= {
83 PLDHashTable::HashVoidPtrKeyStub
, RequestHashMatchEntry
,
84 PLDHashTable::MoveEntryStub
, RequestHashClearEntry
, RequestHashInitEntry
};
86 static void RescheduleRequest(nsIRequest
* aRequest
, int32_t delta
) {
87 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(aRequest
);
88 if (p
) p
->AdjustPriority(delta
);
91 nsLoadGroup::nsLoadGroup()
92 : mRequests(&sRequestHashOps
, sizeof(RequestMapEntry
)) {
93 LOG(("LOADGROUP [%p]: Created.\n", this));
96 nsLoadGroup::~nsLoadGroup() {
97 DebugOnly
<nsresult
> rv
=
98 CancelWithReason(NS_BINDING_ABORTED
, "nsLoadGroup::~nsLoadGroup"_ns
);
99 NS_ASSERTION(NS_SUCCEEDED(rv
), "Cancel failed");
101 mDefaultLoadRequest
= nullptr;
103 if (mRequestContext
&& !mExternalRequestContext
) {
104 mRequestContextService
->RemoveRequestContext(mRequestContext
->GetID());
105 if (IsNeckoChild() && gNeckoChild
) {
106 gNeckoChild
->SendRemoveRequestContext(mRequestContext
->GetID());
110 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
112 Unused
<< os
->RemoveObserver(this, "last-pb-context-exited");
115 LOG(("LOADGROUP [%p]: Destroyed.\n", this));
118 ////////////////////////////////////////////////////////////////////////////////
119 // nsISupports methods:
121 NS_IMPL_ISUPPORTS(nsLoadGroup
, nsILoadGroup
, nsILoadGroupChild
, nsIRequest
,
122 nsISupportsPriority
, nsISupportsWeakReference
, nsIObserver
)
124 ////////////////////////////////////////////////////////////////////////////////
125 // nsIRequest methods:
128 nsLoadGroup::GetName(nsACString
& result
) {
129 // XXX is this the right "name" for a load group?
131 if (!mDefaultLoadRequest
) {
136 return mDefaultLoadRequest
->GetName(result
);
140 nsLoadGroup::IsPending(bool* aResult
) {
141 *aResult
= mForegroundCount
> 0;
146 nsLoadGroup::GetStatus(nsresult
* status
) {
147 if (NS_SUCCEEDED(mStatus
) && mDefaultLoadRequest
) {
148 return mDefaultLoadRequest
->GetStatus(status
);
155 static bool AppendRequestsToArray(PLDHashTable
* aTable
,
156 nsTArray
<nsIRequest
*>* aArray
) {
157 for (auto iter
= aTable
->Iter(); !iter
.Done(); iter
.Next()) {
158 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
159 nsIRequest
* request
= e
->mKey
;
160 MOZ_DIAGNOSTIC_ASSERT(request
, "Null key in mRequests PLDHashTable entry");
162 // XXX(Bug 1631371) Check if this should use a fallible operation as it
163 // pretended earlier.
164 aArray
->AppendElement(request
);
168 if (aArray
->Length() != aTable
->EntryCount()) {
169 for (uint32_t i
= 0, len
= aArray
->Length(); i
< len
; ++i
) {
170 NS_RELEASE((*aArray
)[i
]);
177 NS_IMETHODIMP
nsLoadGroup::SetCanceledReason(const nsACString
& aReason
) {
178 return SetCanceledReasonImpl(aReason
);
181 NS_IMETHODIMP
nsLoadGroup::GetCanceledReason(nsACString
& aReason
) {
182 return GetCanceledReasonImpl(aReason
);
185 NS_IMETHODIMP
nsLoadGroup::CancelWithReason(nsresult aStatus
,
186 const nsACString
& aReason
) {
187 return CancelWithReasonImpl(aStatus
, aReason
);
191 nsLoadGroup::Cancel(nsresult status
) {
192 MOZ_ASSERT(NS_IsMainThread());
194 NS_ASSERTION(NS_FAILED(status
), "shouldn't cancel with a success code");
196 uint32_t count
= mRequests
.EntryCount();
198 AutoTArray
<nsIRequest
*, 8> requests
;
200 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
201 return NS_ERROR_OUT_OF_MEMORY
;
204 // set the load group status to our cancel status while we cancel
205 // all our requests...once the cancel is done, we'll reset it...
209 // Set the flag indicating that the loadgroup is being canceled... This
210 // prevents any new channels from being added during the operation.
214 nsresult firstError
= NS_OK
;
216 nsCOMPtr
<nsIRequest
> request
= requests
.ElementAt(--count
);
218 NS_ASSERTION(request
, "NULL request found in list.");
220 if (!mRequests
.Search(request
)) {
221 // |request| was removed already
222 // We need to null out the entry in the request array so we don't try
223 // to notify the observers for this request.
224 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(count
));
225 requests
.ElementAt(count
) = nullptr;
230 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
231 nsAutoCString nameStr
;
232 request
->GetName(nameStr
);
233 LOG(("LOADGROUP [%p]: Canceling request %p %s.\n", this, request
.get(),
237 // Cancel the request...
238 rv
= request
->CancelWithReason(status
, mCanceledReason
);
240 // Remember the first failure and return it...
241 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
243 if (NS_FAILED(RemoveRequestFromHashtable(request
, status
))) {
244 // It's possible that request->Cancel causes the request to be removed
245 // from the loadgroup causing RemoveRequestFromHashtable to fail.
246 // In that case we shouldn't call NotifyRemovalObservers or decrement
247 // mForegroundCount since that has already happened.
248 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(count
));
249 requests
.ElementAt(count
) = nullptr;
255 for (count
= requests
.Length(); count
> 0;) {
256 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
257 (void)NotifyRemovalObservers(request
, status
);
260 if (mRequestContext
) {
261 Unused
<< mRequestContext
->CancelTailPendingRequests(status
);
265 NS_ASSERTION(mRequests
.EntryCount() == 0, "Request list is not empty.");
266 NS_ASSERTION(mForegroundCount
== 0, "Foreground URLs are active.");
270 mIsCanceling
= false;
276 nsLoadGroup::Suspend() {
277 nsresult rv
, firstError
;
278 uint32_t count
= mRequests
.EntryCount();
280 AutoTArray
<nsIRequest
*, 8> requests
;
282 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
283 return NS_ERROR_OUT_OF_MEMORY
;
288 // Operate the elements from back to front so that if items get
289 // get removed from the list it won't affect our iteration
292 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
294 NS_ASSERTION(request
, "NULL request found in list.");
295 if (!request
) continue;
297 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
298 nsAutoCString nameStr
;
299 request
->GetName(nameStr
);
300 LOG(("LOADGROUP [%p]: Suspending request %p %s.\n", this, request
.get(),
304 // Suspend the request...
305 rv
= request
->Suspend();
307 // Remember the first failure and return it...
308 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
315 nsLoadGroup::Resume() {
316 nsresult rv
, firstError
;
317 uint32_t count
= mRequests
.EntryCount();
319 AutoTArray
<nsIRequest
*, 8> requests
;
321 if (!AppendRequestsToArray(&mRequests
, &requests
)) {
322 return NS_ERROR_OUT_OF_MEMORY
;
327 // Operate the elements from back to front so that if items get
328 // get removed from the list it won't affect our iteration
331 nsCOMPtr
<nsIRequest
> request
= dont_AddRef(requests
.ElementAt(--count
));
333 NS_ASSERTION(request
, "NULL request found in list.");
334 if (!request
) continue;
336 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
337 nsAutoCString nameStr
;
338 request
->GetName(nameStr
);
339 LOG(("LOADGROUP [%p]: Resuming request %p %s.\n", this, request
.get(),
343 // Resume the request...
344 rv
= request
->Resume();
346 // Remember the first failure and return it...
347 if (NS_FAILED(rv
) && NS_SUCCEEDED(firstError
)) firstError
= rv
;
354 nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags
) {
355 *aLoadFlags
= mLoadFlags
;
360 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags
) {
361 mLoadFlags
= aLoadFlags
;
366 nsLoadGroup::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
367 return GetTRRModeImpl(aTRRMode
);
371 nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
372 return SetTRRModeImpl(aTRRMode
);
376 nsLoadGroup::GetLoadGroup(nsILoadGroup
** loadGroup
) {
377 nsCOMPtr
<nsILoadGroup
> result
= mLoadGroup
;
378 result
.forget(loadGroup
);
383 nsLoadGroup::SetLoadGroup(nsILoadGroup
* loadGroup
) {
384 mLoadGroup
= loadGroup
;
388 ////////////////////////////////////////////////////////////////////////////////
389 // nsILoadGroup methods:
392 nsLoadGroup::GetDefaultLoadRequest(nsIRequest
** aRequest
) {
393 nsCOMPtr
<nsIRequest
> result
= mDefaultLoadRequest
;
394 result
.forget(aRequest
);
399 nsLoadGroup::SetDefaultLoadRequest(nsIRequest
* aRequest
) {
400 LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this,
403 mDefaultLoadRequest
= aRequest
;
404 // Inherit the group load flags from the default load request
405 if (mDefaultLoadRequest
) {
406 mDefaultLoadRequest
->GetLoadFlags(&mLoadFlags
);
408 // Mask off any bits that are not part of the nsIRequest flags.
409 // in particular, nsIChannel::LOAD_DOCUMENT_URI...
411 mLoadFlags
&= nsIRequest::LOAD_REQUESTMASK
;
413 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(aRequest
);
414 mDefaultLoadIsTimed
= timedChannel
!= nullptr;
415 if (mDefaultLoadIsTimed
) {
416 timedChannel
->GetChannelCreation(&mDefaultRequestCreationTime
);
417 timedChannel
->SetTimingEnabled(true);
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 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(request
);
473 if (timedChannel
) timedChannel
->SetTimingEnabled(true);
475 bool foreground
= !(flags
& nsIRequest::LOAD_BACKGROUND
);
477 // Update the count of foreground URIs..
478 mForegroundCount
+= 1;
481 if (foreground
|| mNotifyObserverAboutBackgroundRequests
) {
483 // Fire the OnStartRequest notification out to the observer...
485 // If the notification fails then DO NOT add the request to
488 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
491 ("LOADGROUP [%p]: Firing OnStartRequest for request %p."
492 "(foreground count=%d).\n",
493 this, request
, mForegroundCount
));
495 rv
= observer
->OnStartRequest(request
);
497 LOG(("LOADGROUP [%p]: OnStartRequest for request %p FAILED.\n", this,
500 // The URI load has been canceled by the observer. Clean up
504 mRequests
.Remove(request
);
509 mForegroundCount
-= 1;
514 // Ensure that we're part of our loadgroup while pending
515 if (foreground
&& mForegroundCount
== 1 && mLoadGroup
) {
516 mLoadGroup
->AddRequest(this, nullptr);
524 nsLoadGroup::RemoveRequest(nsIRequest
* request
, nsISupports
* ctxt
,
526 // Make sure we have a owning reference to the request we're about
528 nsCOMPtr
<nsIRequest
> kungFuDeathGrip(request
);
530 nsresult rv
= RemoveRequestFromHashtable(request
, aStatus
);
535 return NotifyRemovalObservers(request
, aStatus
);
538 nsresult
nsLoadGroup::RemoveRequestFromHashtable(nsIRequest
* request
,
540 NS_ENSURE_ARG_POINTER(request
);
543 if (MOZ_LOG_TEST(gLoadGroupLog
, LogLevel::Debug
)) {
544 nsAutoCString nameStr
;
545 request
->GetName(nameStr
);
546 LOG(("LOADGROUP [%p]: Removing request %p %s status %" PRIx32
548 this, request
, nameStr
.get(), static_cast<uint32_t>(aStatus
),
549 mRequests
.EntryCount() - 1));
553 // Remove the request from the group. If this fails, it means that
554 // the request was *not* in the group so do not update the foreground
555 // count or it will get messed up...
557 auto* entry
= static_cast<RequestMapEntry
*>(mRequests
.Search(request
));
560 LOG(("LOADGROUP [%p]: Unable to remove request %p. Not in group!\n", this,
563 return NS_ERROR_FAILURE
;
566 mRequests
.RemoveEntry(entry
);
568 // Collect telemetry stats only when default request is a timed channel.
569 // Don't include failed requests in the timing statistics.
570 if (mDefaultLoadIsTimed
&& NS_SUCCEEDED(aStatus
)) {
571 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(request
);
573 // Figure out if this request was served from the cache
576 rv
= timedChannel
->GetCacheReadStart(&timeStamp
);
577 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
581 rv
= timedChannel
->GetAsyncOpen(&timeStamp
);
582 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
583 Telemetry::AccumulateTimeDelta(
584 Telemetry::HTTP_SUBITEM_OPEN_LATENCY_TIME
,
585 mDefaultRequestCreationTime
, timeStamp
);
588 rv
= timedChannel
->GetResponseStart(&timeStamp
);
589 if (NS_SUCCEEDED(rv
) && !timeStamp
.IsNull()) {
590 Telemetry::AccumulateTimeDelta(
591 Telemetry::HTTP_SUBITEM_FIRST_BYTE_LATENCY_TIME
,
592 mDefaultRequestCreationTime
, timeStamp
);
595 TelemetryReportChannel(timedChannel
, false);
599 if (mRequests
.EntryCount() == 0) {
606 nsresult
nsLoadGroup::NotifyRemovalObservers(nsIRequest
* request
,
608 NS_ENSURE_ARG_POINTER(request
);
609 // Undo any group priority delta...
610 if (mPriority
!= 0) RescheduleRequest(request
, -mPriority
);
613 nsresult rv
= request
->GetLoadFlags(&flags
);
614 if (NS_FAILED(rv
)) return rv
;
616 bool foreground
= !(flags
& nsIRequest::LOAD_BACKGROUND
);
618 NS_ASSERTION(mForegroundCount
> 0, "ForegroundCount messed up");
619 mForegroundCount
-= 1;
622 if (foreground
|| mNotifyObserverAboutBackgroundRequests
) {
623 // Fire the OnStopRequest out to the observer...
624 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
627 ("LOADGROUP [%p]: Firing OnStopRequest for request %p."
628 "(foreground count=%d).\n",
629 this, request
, mForegroundCount
));
631 rv
= observer
->OnStopRequest(request
, aStatus
);
634 LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this,
639 // If that was the last request -> remove ourselves from loadgroup
640 if (foreground
&& mForegroundCount
== 0 && mLoadGroup
) {
641 mLoadGroup
->RemoveRequest(this, nullptr, aStatus
);
649 nsLoadGroup::GetRequests(nsISimpleEnumerator
** aRequests
) {
650 nsCOMArray
<nsIRequest
> requests
;
651 requests
.SetCapacity(mRequests
.EntryCount());
653 for (auto iter
= mRequests
.Iter(); !iter
.Done(); iter
.Next()) {
654 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
655 requests
.AppendObject(e
->mKey
);
658 return NS_NewArrayEnumerator(aRequests
, requests
, NS_GET_IID(nsIRequest
));
662 nsLoadGroup::SetGroupObserver(nsIRequestObserver
* aObserver
) {
663 SetGroupObserver(aObserver
, false);
667 void nsLoadGroup::SetGroupObserver(nsIRequestObserver
* aObserver
,
668 bool aIncludeBackgroundRequests
) {
669 mObserver
= do_GetWeakReference(aObserver
);
670 mNotifyObserverAboutBackgroundRequests
= aIncludeBackgroundRequests
;
674 nsLoadGroup::GetGroupObserver(nsIRequestObserver
** aResult
) {
675 nsCOMPtr
<nsIRequestObserver
> observer
= do_QueryReferent(mObserver
);
676 observer
.forget(aResult
);
681 nsLoadGroup::GetActiveCount(uint32_t* aResult
) {
682 *aResult
= mForegroundCount
;
687 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor
** aCallbacks
) {
688 NS_ENSURE_ARG_POINTER(aCallbacks
);
689 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
= mCallbacks
;
690 callbacks
.forget(aCallbacks
);
695 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
) {
696 mCallbacks
= aCallbacks
;
701 nsLoadGroup::GetRequestContextID(uint64_t* aRCID
) {
702 if (!mRequestContext
) {
703 return NS_ERROR_NOT_AVAILABLE
;
705 *aRCID
= mRequestContext
->GetID();
709 ////////////////////////////////////////////////////////////////////////////////
710 // nsILoadGroupChild methods:
713 nsLoadGroup::GetParentLoadGroup(nsILoadGroup
** aParentLoadGroup
) {
714 *aParentLoadGroup
= nullptr;
715 nsCOMPtr
<nsILoadGroup
> parent
= do_QueryReferent(mParentLoadGroup
);
716 if (!parent
) return NS_OK
;
717 parent
.forget(aParentLoadGroup
);
722 nsLoadGroup::SetParentLoadGroup(nsILoadGroup
* aParentLoadGroup
) {
723 mParentLoadGroup
= do_GetWeakReference(aParentLoadGroup
);
728 nsLoadGroup::GetChildLoadGroup(nsILoadGroup
** aChildLoadGroup
) {
729 *aChildLoadGroup
= do_AddRef(this).take();
734 nsLoadGroup::GetRootLoadGroup(nsILoadGroup
** aRootLoadGroup
) {
735 // first recursively try the root load group of our parent
736 nsCOMPtr
<nsILoadGroupChild
> ancestor
= do_QueryReferent(mParentLoadGroup
);
737 if (ancestor
) return ancestor
->GetRootLoadGroup(aRootLoadGroup
);
739 // next recursively try the root load group of our own load grop
740 ancestor
= do_QueryInterface(mLoadGroup
);
741 if (ancestor
) return ancestor
->GetRootLoadGroup(aRootLoadGroup
);
743 // finally just return this
744 *aRootLoadGroup
= do_AddRef(this).take();
748 ////////////////////////////////////////////////////////////////////////////////
749 // nsISupportsPriority methods:
752 nsLoadGroup::GetPriority(int32_t* aValue
) {
758 nsLoadGroup::SetPriority(int32_t aValue
) {
759 return AdjustPriority(aValue
- mPriority
);
763 nsLoadGroup::AdjustPriority(int32_t aDelta
) {
764 // Update the priority for each request that supports nsISupportsPriority
767 for (auto iter
= mRequests
.Iter(); !iter
.Done(); iter
.Next()) {
768 auto* e
= static_cast<RequestMapEntry
*>(iter
.Get());
769 RescheduleRequest(e
->mKey
, aDelta
);
776 nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags
) {
777 *aFlags
= mDefaultLoadFlags
;
782 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags
) {
783 mDefaultLoadFlags
= aFlags
;
787 ////////////////////////////////////////////////////////////////////////////////
789 void nsLoadGroup::TelemetryReport() {
790 nsresult defaultStatus
= NS_ERROR_INVALID_ARG
;
791 // We should only report HTTP_PAGE_* telemetry if the defaultRequest was
792 // actually successful.
793 if (mDefaultLoadRequest
) {
794 mDefaultLoadRequest
->GetStatus(&defaultStatus
);
796 if (mDefaultLoadIsTimed
&& NS_SUCCEEDED(defaultStatus
)) {
797 Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE
, mTimedRequests
);
798 if (mTimedRequests
) {
799 Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE_FROM_CACHE
,
800 mCachedRequests
* 100 / mTimedRequests
);
803 nsCOMPtr
<nsITimedChannel
> timedChannel
=
804 do_QueryInterface(mDefaultLoadRequest
);
805 if (timedChannel
) TelemetryReportChannel(timedChannel
, true);
810 mDefaultLoadIsTimed
= false;
813 void nsLoadGroup::TelemetryReportChannel(nsITimedChannel
* aTimedChannel
,
814 bool aDefaultRequest
) {
817 rv
= aTimedChannel
->GetTimingEnabled(&timingEnabled
);
818 if (NS_FAILED(rv
) || !timingEnabled
) return;
821 rv
= aTimedChannel
->GetAsyncOpen(&asyncOpen
);
822 // We do not check !asyncOpen.IsNull() bellow, prevent ASSERTIONs this way
823 if (NS_FAILED(rv
) || asyncOpen
.IsNull()) return;
825 TimeStamp cacheReadStart
;
826 rv
= aTimedChannel
->GetCacheReadStart(&cacheReadStart
);
827 if (NS_FAILED(rv
)) return;
829 TimeStamp cacheReadEnd
;
830 rv
= aTimedChannel
->GetCacheReadEnd(&cacheReadEnd
);
831 if (NS_FAILED(rv
)) return;
833 TimeStamp domainLookupStart
;
834 rv
= aTimedChannel
->GetDomainLookupStart(&domainLookupStart
);
835 if (NS_FAILED(rv
)) return;
837 TimeStamp domainLookupEnd
;
838 rv
= aTimedChannel
->GetDomainLookupEnd(&domainLookupEnd
);
839 if (NS_FAILED(rv
)) return;
841 TimeStamp connectStart
;
842 rv
= aTimedChannel
->GetConnectStart(&connectStart
);
843 if (NS_FAILED(rv
)) return;
845 TimeStamp secureConnectionStart
;
846 rv
= aTimedChannel
->GetSecureConnectionStart(&secureConnectionStart
);
847 if (NS_FAILED(rv
)) return;
849 TimeStamp connectEnd
;
850 rv
= aTimedChannel
->GetConnectEnd(&connectEnd
);
851 if (NS_FAILED(rv
)) return;
853 TimeStamp requestStart
;
854 rv
= aTimedChannel
->GetRequestStart(&requestStart
);
855 if (NS_FAILED(rv
)) return;
857 TimeStamp responseStart
;
858 rv
= aTimedChannel
->GetResponseStart(&responseStart
);
859 if (NS_FAILED(rv
)) return;
861 TimeStamp responseEnd
;
862 rv
= aTimedChannel
->GetResponseEnd(&responseEnd
);
863 if (NS_FAILED(rv
)) return;
865 bool useHttp3
= false;
866 bool supportHttp3
= false;
867 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
=
868 do_QueryInterface(aTimedChannel
);
872 if (NS_SUCCEEDED(httpChannel
->GetResponseVersion(&major
, &minor
))) {
873 useHttp3
= major
== 3;
875 if (NS_FAILED(httpChannel
->GetSupportsHTTP3(&supportHttp3
))) {
876 supportHttp3
= false;
882 #define HTTP_REQUEST_HISTOGRAMS(prefix) \
883 if (!domainLookupStart.IsNull()) { \
884 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_DNS_ISSUE_TIME, \
885 asyncOpen, domainLookupStart); \
888 if (!domainLookupStart.IsNull() && !domainLookupEnd.IsNull()) { \
889 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_DNS_LOOKUP_TIME, \
890 domainLookupStart, domainLookupEnd); \
893 if (!secureConnectionStart.IsNull() && !connectEnd.IsNull()) { \
894 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_TLS_HANDSHAKE, \
895 secureConnectionStart, connectEnd); \
898 if (!connectStart.IsNull() && !connectEnd.IsNull()) { \
899 Telemetry::AccumulateTimeDelta( \
900 Telemetry::HTTP_##prefix##_TCP_CONNECTION_2, connectStart, \
904 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
905 Telemetry::AccumulateTimeDelta( \
906 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_SENT, asyncOpen, \
909 Telemetry::AccumulateTimeDelta( \
910 Telemetry::HTTP_##prefix##_FIRST_SENT_TO_LAST_RECEIVED, requestStart, \
913 if (cacheReadStart.IsNull() && !responseStart.IsNull()) { \
914 Telemetry::AccumulateTimeDelta( \
915 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_RECEIVED, asyncOpen, \
920 if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) { \
921 Telemetry::AccumulateTimeDelta( \
922 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_FROM_CACHE_V2, asyncOpen, \
925 Telemetry::AccumulateTimeDelta( \
926 Telemetry::HTTP_##prefix##_CACHE_READ_TIME_V2, cacheReadStart, \
929 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
930 Telemetry::AccumulateTimeDelta(Telemetry::HTTP_##prefix##_REVALIDATION, \
931 requestStart, responseEnd); \
935 if (!cacheReadEnd.IsNull()) { \
936 Telemetry::AccumulateTimeDelta( \
937 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_V2, asyncOpen, cacheReadEnd); \
938 Telemetry::AccumulateTimeDelta( \
939 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_CACHED_V2, asyncOpen, \
941 } else if (!responseEnd.IsNull()) { \
942 Telemetry::AccumulateTimeDelta( \
943 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_V2, asyncOpen, responseEnd); \
944 Telemetry::AccumulateTimeDelta( \
945 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_NET_V2, asyncOpen, \
949 if (aDefaultRequest
) {
950 HTTP_REQUEST_HISTOGRAMS(PAGE
)
952 HTTP_REQUEST_HISTOGRAMS(SUB
)
955 if ((useHttp3
|| supportHttp3
) && cacheReadStart
.IsNull() &&
956 cacheReadEnd
.IsNull()) {
957 nsCString key
= (useHttp3
) ? ((aDefaultRequest
) ? "uses_http3_page"_ns
958 : "uses_http3_sub"_ns
)
959 : ((aDefaultRequest
) ? "supports_http3_page"_ns
960 : "supports_http3_sub"_ns
);
962 if (!secureConnectionStart
.IsNull() && !connectEnd
.IsNull()) {
963 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_TLS_HANDSHAKE
, key
,
964 secureConnectionStart
, connectEnd
);
967 if (supportHttp3
&& !connectStart
.IsNull() && !connectEnd
.IsNull()) {
968 Telemetry::AccumulateTimeDelta(Telemetry::SUP_HTTP3_TCP_CONNECTION
, key
,
969 connectStart
, connectEnd
);
972 if (!requestStart
.IsNull() && !responseEnd
.IsNull()) {
973 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_OPEN_TO_FIRST_SENT
, key
,
974 asyncOpen
, requestStart
);
976 Telemetry::AccumulateTimeDelta(
977 Telemetry::HTTP3_FIRST_SENT_TO_LAST_RECEIVED
, key
, requestStart
,
980 if (!responseStart
.IsNull()) {
981 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_OPEN_TO_FIRST_RECEIVED
,
982 key
, asyncOpen
, responseStart
);
985 if (!responseEnd
.IsNull()) {
986 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_COMPLETE_LOAD
, key
,
987 asyncOpen
, responseEnd
);
992 bool hasHTTPSRR
= false;
993 if (httpChannel
&& NS_SUCCEEDED(httpChannel
->GetHasHTTPSRR(&hasHTTPSRR
)) &&
994 cacheReadStart
.IsNull() && cacheReadEnd
.IsNull() &&
995 !requestStart
.IsNull()) {
996 nsCString key
= (hasHTTPSRR
) ? ((aDefaultRequest
) ? "uses_https_rr_page"_ns
997 : "uses_https_rr_sub"_ns
)
998 : ((aDefaultRequest
) ? "no_https_rr_page"_ns
999 : "no_https_rr_sub"_ns
);
1000 Telemetry::AccumulateTimeDelta(Telemetry::HTTPS_RR_OPEN_TO_FIRST_SENT
, key
,
1001 asyncOpen
, requestStart
);
1004 #undef HTTP_REQUEST_HISTOGRAMS
1007 nsresult
nsLoadGroup::MergeLoadFlags(nsIRequest
* aRequest
,
1008 nsLoadFlags
& outFlags
) {
1010 nsLoadFlags flags
, oldFlags
;
1012 rv
= aRequest
->GetLoadFlags(&flags
);
1013 if (NS_FAILED(rv
)) {
1019 // Inherit the following bits...
1020 flags
|= (mLoadFlags
&
1021 (LOAD_BACKGROUND
| LOAD_BYPASS_CACHE
| LOAD_FROM_CACHE
|
1022 VALIDATE_ALWAYS
| VALIDATE_ONCE_PER_SESSION
| VALIDATE_NEVER
));
1024 // ... and force the default flags.
1025 flags
|= mDefaultLoadFlags
;
1027 if (flags
!= oldFlags
) {
1028 rv
= aRequest
->SetLoadFlags(flags
);
1035 nsresult
nsLoadGroup::MergeDefaultLoadFlags(nsIRequest
* aRequest
,
1036 nsLoadFlags
& outFlags
) {
1038 nsLoadFlags flags
, oldFlags
;
1040 rv
= aRequest
->GetLoadFlags(&flags
);
1041 if (NS_FAILED(rv
)) {
1046 // ... and force the default flags.
1047 flags
|= mDefaultLoadFlags
;
1049 if (flags
!= oldFlags
) {
1050 rv
= aRequest
->SetLoadFlags(flags
);
1056 nsresult
nsLoadGroup::Init() {
1057 mRequestContextService
= RequestContextService::GetOrCreate();
1058 if (mRequestContextService
) {
1059 Unused
<< mRequestContextService
->NewRequestContext(
1060 getter_AddRefs(mRequestContext
));
1063 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1064 NS_ENSURE_STATE(os
);
1066 Unused
<< os
->AddObserver(this, "last-pb-context-exited", true);
1071 nsresult
nsLoadGroup::InitWithRequestContextId(
1072 const uint64_t& aRequestContextId
) {
1073 mRequestContextService
= RequestContextService::GetOrCreate();
1074 if (mRequestContextService
) {
1075 Unused
<< mRequestContextService
->GetRequestContext(
1076 aRequestContextId
, getter_AddRefs(mRequestContext
));
1078 mExternalRequestContext
= true;
1080 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1081 NS_ENSURE_STATE(os
);
1083 Unused
<< os
->AddObserver(this, "last-pb-context-exited", true);
1089 nsLoadGroup::Observe(nsISupports
* aSubject
, const char* aTopic
,
1090 const char16_t
* aData
) {
1091 MOZ_ASSERT(!strcmp(aTopic
, "last-pb-context-exited"));
1093 OriginAttributes attrs
;
1094 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs
);
1095 if (attrs
.mPrivateBrowsingId
== 0) {
1099 mBrowsingContextDiscarded
= true;
1104 nsLoadGroup::GetIsBrowsingContextDiscarded(bool* aIsBrowsingContextDiscarded
) {
1105 *aIsBrowsingContextDiscarded
= mBrowsingContextDiscarded
;
1110 } // namespace mozilla