Bug 1804798 - Explicitly set auto page name (and corresponding debug flag) when inser...
[gecko.git] / netwerk / base / nsLoadGroup.cpp
bloba67273854e1d4f1013d5b4789fa2525b87c6708e
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"
13 #include "nsCOMPtr.h"
14 #include "nsContentUtils.h"
15 #include "mozilla/Logging.h"
16 #include "nsString.h"
17 #include "nsTArray.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"
29 namespace mozilla {
30 namespace net {
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");
44 #undef LOG
45 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
47 ////////////////////////////////////////////////////////////////////////////////
49 class RequestMapEntry : public PLDHashEntryHdr {
50 public:
51 explicit RequestMapEntry(nsIRequest* aRequest) : mKey(aRequest) {}
53 nsCOMPtr<nsIRequest> mKey;
56 static bool RequestHashMatchEntry(const PLDHashEntryHdr* entry,
57 const void* key) {
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();
108 if (os) {
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:
124 NS_IMETHODIMP
125 nsLoadGroup::GetName(nsACString& result) {
126 // XXX is this the right "name" for a load group?
128 if (!mDefaultLoadRequest) {
129 result.Truncate();
130 return NS_OK;
133 return mDefaultLoadRequest->GetName(result);
136 NS_IMETHODIMP
137 nsLoadGroup::IsPending(bool* aResult) {
138 *aResult = mForegroundCount > 0;
139 return NS_OK;
142 NS_IMETHODIMP
143 nsLoadGroup::GetStatus(nsresult* status) {
144 if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest) {
145 return mDefaultLoadRequest->GetStatus(status);
148 *status = mStatus;
149 return NS_OK;
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);
162 NS_ADDREF(request);
165 if (aArray->Length() != aTable->EntryCount()) {
166 for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
167 NS_RELEASE((*aArray)[i]);
169 return false;
171 return true;
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);
187 NS_IMETHODIMP
188 nsLoadGroup::Cancel(nsresult status) {
189 MOZ_ASSERT(NS_IsMainThread());
191 NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
192 nsresult rv;
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...
204 mStatus = status;
206 // Set the flag indicating that the loadgroup is being canceled... This
207 // prevents any new channels from being added during the operation.
209 mIsCanceling = true;
211 nsresult firstError = NS_OK;
212 while (count > 0) {
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;
224 continue;
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(),
231 nameStr.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;
248 continue;
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);
261 #if defined(DEBUG)
262 NS_ASSERTION(mRequests.EntryCount() == 0, "Request list is not empty.");
263 NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");
264 #endif
266 mStatus = NS_OK;
267 mIsCanceling = false;
269 return firstError;
272 NS_IMETHODIMP
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;
283 firstError = NS_OK;
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
288 while (count > 0) {
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(),
298 nameStr.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;
308 return firstError;
311 NS_IMETHODIMP
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;
322 firstError = NS_OK;
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
327 while (count > 0) {
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(),
337 nameStr.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;
347 return firstError;
350 NS_IMETHODIMP
351 nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags) {
352 *aLoadFlags = mLoadFlags;
353 return NS_OK;
356 NS_IMETHODIMP
357 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags) {
358 mLoadFlags = aLoadFlags;
359 return NS_OK;
362 NS_IMETHODIMP
363 nsLoadGroup::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
364 return GetTRRModeImpl(aTRRMode);
367 NS_IMETHODIMP
368 nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
369 return SetTRRModeImpl(aTRRMode);
372 NS_IMETHODIMP
373 nsLoadGroup::GetLoadGroup(nsILoadGroup** loadGroup) {
374 nsCOMPtr<nsILoadGroup> result = mLoadGroup;
375 result.forget(loadGroup);
376 return NS_OK;
379 NS_IMETHODIMP
380 nsLoadGroup::SetLoadGroup(nsILoadGroup* loadGroup) {
381 mLoadGroup = loadGroup;
382 return NS_OK;
385 ////////////////////////////////////////////////////////////////////////////////
386 // nsILoadGroup methods:
388 NS_IMETHODIMP
389 nsLoadGroup::GetDefaultLoadRequest(nsIRequest** aRequest) {
390 nsCOMPtr<nsIRequest> result = mDefaultLoadRequest;
391 result.forget(aRequest);
392 return NS_OK;
395 NS_IMETHODIMP
396 nsLoadGroup::SetDefaultLoadRequest(nsIRequest* aRequest) {
397 LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this,
398 aRequest));
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)
418 return NS_OK;
421 NS_IMETHODIMP
422 nsLoadGroup::AddRequest(nsIRequest* request, nsISupports* ctxt) {
423 nsresult rv;
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...
438 if (mIsCanceling) {
439 LOG(
440 ("LOADGROUP [%p]: AddChannel() ABORTED because LoadGroup is"
441 " being canceled!!\n",
442 this));
444 return NS_BINDING_ABORTED;
447 nsLoadFlags flags;
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);
453 } else {
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));
463 if (!entry) {
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);
473 if (foreground) {
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
483 // the load group.
485 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
486 if (observer) {
487 LOG(
488 ("LOADGROUP [%p]: Firing OnStartRequest for request %p."
489 "(foreground count=%d).\n",
490 this, request, mForegroundCount));
492 rv = observer->OnStartRequest(request);
493 if (NS_FAILED(rv)) {
494 LOG(("LOADGROUP [%p]: OnStartRequest for request %p FAILED.\n", this,
495 request));
497 // The URI load has been canceled by the observer. Clean up
498 // the damage...
501 mRequests.Remove(request);
503 rv = NS_OK;
505 if (foreground) {
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);
517 return rv;
520 NS_IMETHODIMP
521 nsLoadGroup::RemoveRequest(nsIRequest* request, nsISupports* ctxt,
522 nsresult aStatus) {
523 // Make sure we have a owning reference to the request we're about
524 // to remove.
525 nsCOMPtr<nsIRequest> kungFuDeathGrip(request);
527 nsresult rv = RemoveRequestFromHashtable(request, aStatus);
528 if (NS_FAILED(rv)) {
529 return rv;
532 return NotifyRemovalObservers(request, aStatus);
535 nsresult nsLoadGroup::RemoveRequestFromHashtable(nsIRequest* request,
536 nsresult aStatus) {
537 NS_ENSURE_ARG_POINTER(request);
538 nsresult rv;
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
544 " (count=%d).\n",
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));
556 if (!entry) {
557 LOG(("LOADGROUP [%p]: Unable to remove request %p. Not in group!\n", this,
558 request));
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);
569 if (timedChannel) {
570 // Figure out if this request was served from the cache
571 ++mTimedRequests;
572 TimeStamp timeStamp;
573 rv = timedChannel->GetCacheReadStart(&timeStamp);
574 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
575 ++mCachedRequests;
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) {
597 TelemetryReport();
600 return NS_OK;
603 nsresult nsLoadGroup::NotifyRemovalObservers(nsIRequest* request,
604 nsresult aStatus) {
605 NS_ENSURE_ARG_POINTER(request);
606 // Undo any group priority delta...
607 if (mPriority != 0) RescheduleRequest(request, -mPriority);
609 nsLoadFlags flags;
610 nsresult rv = request->GetLoadFlags(&flags);
611 if (NS_FAILED(rv)) return rv;
613 bool foreground = !(flags & nsIRequest::LOAD_BACKGROUND);
614 if (foreground) {
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);
622 if (observer) {
623 LOG(
624 ("LOADGROUP [%p]: Firing OnStopRequest for request %p."
625 "(foreground count=%d).\n",
626 this, request, mForegroundCount));
628 rv = observer->OnStopRequest(request, aStatus);
630 if (NS_FAILED(rv)) {
631 LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this,
632 request));
636 // If that was the last request -> remove ourselves from loadgroup
637 if (foreground && mForegroundCount == 0 && mLoadGroup) {
638 mLoadGroup->RemoveRequest(this, nullptr, aStatus);
642 return rv;
645 NS_IMETHODIMP
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));
658 NS_IMETHODIMP
659 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) {
660 SetGroupObserver(aObserver, false);
661 return NS_OK;
664 void nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver,
665 bool aIncludeBackgroundRequests) {
666 mObserver = do_GetWeakReference(aObserver);
667 mNotifyObserverAboutBackgroundRequests = aIncludeBackgroundRequests;
670 NS_IMETHODIMP
671 nsLoadGroup::GetGroupObserver(nsIRequestObserver** aResult) {
672 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
673 observer.forget(aResult);
674 return NS_OK;
677 NS_IMETHODIMP
678 nsLoadGroup::GetActiveCount(uint32_t* aResult) {
679 *aResult = mForegroundCount;
680 return NS_OK;
683 NS_IMETHODIMP
684 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
685 NS_ENSURE_ARG_POINTER(aCallbacks);
686 nsCOMPtr<nsIInterfaceRequestor> callbacks = mCallbacks;
687 callbacks.forget(aCallbacks);
688 return NS_OK;
691 NS_IMETHODIMP
692 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
693 mCallbacks = aCallbacks;
694 return NS_OK;
697 NS_IMETHODIMP
698 nsLoadGroup::GetRequestContextID(uint64_t* aRCID) {
699 if (!mRequestContext) {
700 return NS_ERROR_NOT_AVAILABLE;
702 *aRCID = mRequestContext->GetID();
703 return NS_OK;
706 ////////////////////////////////////////////////////////////////////////////////
707 // nsILoadGroupChild methods:
709 NS_IMETHODIMP
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);
715 return NS_OK;
718 NS_IMETHODIMP
719 nsLoadGroup::SetParentLoadGroup(nsILoadGroup* aParentLoadGroup) {
720 mParentLoadGroup = do_GetWeakReference(aParentLoadGroup);
721 return NS_OK;
724 NS_IMETHODIMP
725 nsLoadGroup::GetChildLoadGroup(nsILoadGroup** aChildLoadGroup) {
726 *aChildLoadGroup = do_AddRef(this).take();
727 return NS_OK;
730 NS_IMETHODIMP
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();
742 return NS_OK;
745 ////////////////////////////////////////////////////////////////////////////////
746 // nsISupportsPriority methods:
748 NS_IMETHODIMP
749 nsLoadGroup::GetPriority(int32_t* aValue) {
750 *aValue = mPriority;
751 return NS_OK;
754 NS_IMETHODIMP
755 nsLoadGroup::SetPriority(int32_t aValue) {
756 return AdjustPriority(aValue - mPriority);
759 NS_IMETHODIMP
760 nsLoadGroup::AdjustPriority(int32_t aDelta) {
761 // Update the priority for each request that supports nsISupportsPriority
762 if (aDelta != 0) {
763 mPriority += aDelta;
764 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
765 auto* e = static_cast<RequestMapEntry*>(iter.Get());
766 RescheduleRequest(e->mKey, aDelta);
769 return NS_OK;
772 NS_IMETHODIMP
773 nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags) {
774 *aFlags = mDefaultLoadFlags;
775 return NS_OK;
778 NS_IMETHODIMP
779 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags) {
780 mDefaultLoadFlags = aFlags;
781 return NS_OK;
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);
805 mTimedRequests = 0;
806 mCachedRequests = 0;
807 mDefaultLoadIsTimed = false;
810 void nsLoadGroup::TelemetryReportChannel(nsITimedChannel* aTimedChannel,
811 bool aDefaultRequest) {
812 nsresult rv;
813 bool timingEnabled;
814 rv = aTimedChannel->GetTimingEnabled(&timingEnabled);
815 if (NS_FAILED(rv) || !timingEnabled) return;
817 TimeStamp asyncOpen;
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);
866 if (httpChannel) {
867 uint32_t major;
868 uint32_t minor;
869 if (NS_SUCCEEDED(httpChannel->GetResponseVersion(&major, &minor))) {
870 useHttp3 = major == 3;
871 if (major == 2) {
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, \
898 connectEnd); \
901 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
902 Telemetry::AccumulateTimeDelta( \
903 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_SENT, asyncOpen, \
904 requestStart); \
906 Telemetry::AccumulateTimeDelta( \
907 Telemetry::HTTP_##prefix##_FIRST_SENT_TO_LAST_RECEIVED, requestStart, \
908 responseEnd); \
910 if (cacheReadStart.IsNull() && !responseStart.IsNull()) { \
911 Telemetry::AccumulateTimeDelta( \
912 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_RECEIVED, asyncOpen, \
913 responseStart); \
917 if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) { \
918 Telemetry::AccumulateTimeDelta( \
919 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_FROM_CACHE_V2, asyncOpen, \
920 cacheReadStart); \
922 Telemetry::AccumulateTimeDelta( \
923 Telemetry::HTTP_##prefix##_CACHE_READ_TIME_V2, cacheReadStart, \
924 cacheReadEnd); \
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, \
937 cacheReadEnd); \
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, \
943 responseEnd); \
946 if (aDefaultRequest) {
947 HTTP_REQUEST_HISTOGRAMS(PAGE)
948 } else {
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,
975 responseEnd);
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)) &&
1006 ODoHActivated) {
1007 if (aDefaultRequest) {
1008 Telemetry::AccumulateTimeDelta(
1009 Telemetry::HTTP_PAGE_DNS_ODOH_LOOKUP_TIME, domainLookupStart,
1010 domainLookupEnd);
1011 } else {
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) {
1023 nsresult rv;
1024 nsLoadFlags flags, oldFlags;
1026 rv = aRequest->GetLoadFlags(&flags);
1027 if (NS_FAILED(rv)) {
1028 return rv;
1031 oldFlags = flags;
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);
1045 outFlags = flags;
1046 return rv;
1049 nsresult nsLoadGroup::MergeDefaultLoadFlags(nsIRequest* aRequest,
1050 nsLoadFlags& outFlags) {
1051 nsresult rv;
1052 nsLoadFlags flags, oldFlags;
1054 rv = aRequest->GetLoadFlags(&flags);
1055 if (NS_FAILED(rv)) {
1056 return rv;
1059 oldFlags = flags;
1060 // ... and force the default flags.
1061 flags |= mDefaultLoadFlags;
1063 if (flags != oldFlags) {
1064 rv = aRequest->SetLoadFlags(flags);
1066 outFlags = flags;
1067 return rv;
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);
1082 return NS_OK;
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);
1099 return NS_OK;
1102 NS_IMETHODIMP
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) {
1110 return NS_OK;
1113 mBrowsingContextDiscarded = true;
1114 return NS_OK;
1117 NS_IMETHODIMP
1118 nsLoadGroup::GetIsBrowsingContextDiscarded(bool* aIsBrowsingContextDiscarded) {
1119 *aIsBrowsingContextDiscarded = mBrowsingContextDiscarded;
1120 return NS_OK;
1123 } // namespace net
1124 } // namespace mozilla
1126 #undef LOG