Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / netwerk / base / nsLoadGroup.cpp
blobe3f88fc55467fe684c3f59fe5260c3c81eac3e3c
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"
28 #include "mozilla/net/NeckoCommon.h"
29 #include "mozilla/net/NeckoChild.h"
30 #include "mozilla/StaticPrefs_network.h"
32 namespace mozilla {
33 namespace net {
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");
47 #undef LOG
48 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
50 ////////////////////////////////////////////////////////////////////////////////
52 class RequestMapEntry : public PLDHashEntryHdr {
53 public:
54 explicit RequestMapEntry(nsIRequest* aRequest) : mKey(aRequest) {}
56 nsCOMPtr<nsIRequest> mKey;
59 static bool RequestHashMatchEntry(const PLDHashEntryHdr* entry,
60 const void* key) {
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();
111 if (os) {
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:
127 NS_IMETHODIMP
128 nsLoadGroup::GetName(nsACString& result) {
129 // XXX is this the right "name" for a load group?
131 if (!mDefaultLoadRequest) {
132 result.Truncate();
133 return NS_OK;
136 return mDefaultLoadRequest->GetName(result);
139 NS_IMETHODIMP
140 nsLoadGroup::IsPending(bool* aResult) {
141 *aResult = mForegroundCount > 0;
142 return NS_OK;
145 NS_IMETHODIMP
146 nsLoadGroup::GetStatus(nsresult* status) {
147 if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest) {
148 return mDefaultLoadRequest->GetStatus(status);
151 *status = mStatus;
152 return NS_OK;
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);
165 NS_ADDREF(request);
168 if (aArray->Length() != aTable->EntryCount()) {
169 for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
170 NS_RELEASE((*aArray)[i]);
172 return false;
174 return true;
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);
190 NS_IMETHODIMP
191 nsLoadGroup::Cancel(nsresult status) {
192 MOZ_ASSERT(NS_IsMainThread());
194 NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
195 nsresult rv;
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...
207 mStatus = status;
209 // Set the flag indicating that the loadgroup is being canceled... This
210 // prevents any new channels from being added during the operation.
212 mIsCanceling = true;
214 nsresult firstError = NS_OK;
215 while (count > 0) {
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;
227 continue;
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(),
234 nameStr.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;
251 continue;
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);
264 #if defined(DEBUG)
265 NS_ASSERTION(mRequests.EntryCount() == 0, "Request list is not empty.");
266 NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");
267 #endif
269 mStatus = NS_OK;
270 mIsCanceling = false;
272 return firstError;
275 NS_IMETHODIMP
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;
286 firstError = NS_OK;
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
291 while (count > 0) {
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(),
301 nameStr.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;
311 return firstError;
314 NS_IMETHODIMP
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;
325 firstError = NS_OK;
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
330 while (count > 0) {
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(),
340 nameStr.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;
350 return firstError;
353 NS_IMETHODIMP
354 nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags) {
355 *aLoadFlags = mLoadFlags;
356 return NS_OK;
359 NS_IMETHODIMP
360 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags) {
361 mLoadFlags = aLoadFlags;
362 return NS_OK;
365 NS_IMETHODIMP
366 nsLoadGroup::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
367 return GetTRRModeImpl(aTRRMode);
370 NS_IMETHODIMP
371 nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
372 return SetTRRModeImpl(aTRRMode);
375 NS_IMETHODIMP
376 nsLoadGroup::GetLoadGroup(nsILoadGroup** loadGroup) {
377 nsCOMPtr<nsILoadGroup> result = mLoadGroup;
378 result.forget(loadGroup);
379 return NS_OK;
382 NS_IMETHODIMP
383 nsLoadGroup::SetLoadGroup(nsILoadGroup* loadGroup) {
384 mLoadGroup = loadGroup;
385 return NS_OK;
388 ////////////////////////////////////////////////////////////////////////////////
389 // nsILoadGroup methods:
391 NS_IMETHODIMP
392 nsLoadGroup::GetDefaultLoadRequest(nsIRequest** aRequest) {
393 nsCOMPtr<nsIRequest> result = mDefaultLoadRequest;
394 result.forget(aRequest);
395 return NS_OK;
398 NS_IMETHODIMP
399 nsLoadGroup::SetDefaultLoadRequest(nsIRequest* aRequest) {
400 LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this,
401 aRequest));
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)
421 return NS_OK;
424 NS_IMETHODIMP
425 nsLoadGroup::AddRequest(nsIRequest* request, nsISupports* ctxt) {
426 nsresult rv;
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...
441 if (mIsCanceling) {
442 LOG(
443 ("LOADGROUP [%p]: AddChannel() ABORTED because LoadGroup is"
444 " being canceled!!\n",
445 this));
447 return NS_BINDING_ABORTED;
450 nsLoadFlags flags;
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);
456 } else {
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));
466 if (!entry) {
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);
476 if (foreground) {
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
486 // the load group.
488 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
489 if (observer) {
490 LOG(
491 ("LOADGROUP [%p]: Firing OnStartRequest for request %p."
492 "(foreground count=%d).\n",
493 this, request, mForegroundCount));
495 rv = observer->OnStartRequest(request);
496 if (NS_FAILED(rv)) {
497 LOG(("LOADGROUP [%p]: OnStartRequest for request %p FAILED.\n", this,
498 request));
500 // The URI load has been canceled by the observer. Clean up
501 // the damage...
504 mRequests.Remove(request);
506 rv = NS_OK;
508 if (foreground) {
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);
520 return rv;
523 NS_IMETHODIMP
524 nsLoadGroup::RemoveRequest(nsIRequest* request, nsISupports* ctxt,
525 nsresult aStatus) {
526 // Make sure we have a owning reference to the request we're about
527 // to remove.
528 nsCOMPtr<nsIRequest> kungFuDeathGrip(request);
530 nsresult rv = RemoveRequestFromHashtable(request, aStatus);
531 if (NS_FAILED(rv)) {
532 return rv;
535 return NotifyRemovalObservers(request, aStatus);
538 nsresult nsLoadGroup::RemoveRequestFromHashtable(nsIRequest* request,
539 nsresult aStatus) {
540 NS_ENSURE_ARG_POINTER(request);
541 nsresult rv;
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
547 " (count=%d).\n",
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));
559 if (!entry) {
560 LOG(("LOADGROUP [%p]: Unable to remove request %p. Not in group!\n", this,
561 request));
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);
572 if (timedChannel) {
573 // Figure out if this request was served from the cache
574 ++mTimedRequests;
575 TimeStamp timeStamp;
576 rv = timedChannel->GetCacheReadStart(&timeStamp);
577 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
578 ++mCachedRequests;
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) {
600 TelemetryReport();
603 return NS_OK;
606 nsresult nsLoadGroup::NotifyRemovalObservers(nsIRequest* request,
607 nsresult aStatus) {
608 NS_ENSURE_ARG_POINTER(request);
609 // Undo any group priority delta...
610 if (mPriority != 0) RescheduleRequest(request, -mPriority);
612 nsLoadFlags flags;
613 nsresult rv = request->GetLoadFlags(&flags);
614 if (NS_FAILED(rv)) return rv;
616 bool foreground = !(flags & nsIRequest::LOAD_BACKGROUND);
617 if (foreground) {
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);
625 if (observer) {
626 LOG(
627 ("LOADGROUP [%p]: Firing OnStopRequest for request %p."
628 "(foreground count=%d).\n",
629 this, request, mForegroundCount));
631 rv = observer->OnStopRequest(request, aStatus);
633 if (NS_FAILED(rv)) {
634 LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this,
635 request));
639 // If that was the last request -> remove ourselves from loadgroup
640 if (foreground && mForegroundCount == 0 && mLoadGroup) {
641 mLoadGroup->RemoveRequest(this, nullptr, aStatus);
645 return rv;
648 NS_IMETHODIMP
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));
661 NS_IMETHODIMP
662 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) {
663 SetGroupObserver(aObserver, false);
664 return NS_OK;
667 void nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver,
668 bool aIncludeBackgroundRequests) {
669 mObserver = do_GetWeakReference(aObserver);
670 mNotifyObserverAboutBackgroundRequests = aIncludeBackgroundRequests;
673 NS_IMETHODIMP
674 nsLoadGroup::GetGroupObserver(nsIRequestObserver** aResult) {
675 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
676 observer.forget(aResult);
677 return NS_OK;
680 NS_IMETHODIMP
681 nsLoadGroup::GetActiveCount(uint32_t* aResult) {
682 *aResult = mForegroundCount;
683 return NS_OK;
686 NS_IMETHODIMP
687 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
688 NS_ENSURE_ARG_POINTER(aCallbacks);
689 nsCOMPtr<nsIInterfaceRequestor> callbacks = mCallbacks;
690 callbacks.forget(aCallbacks);
691 return NS_OK;
694 NS_IMETHODIMP
695 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
696 mCallbacks = aCallbacks;
697 return NS_OK;
700 NS_IMETHODIMP
701 nsLoadGroup::GetRequestContextID(uint64_t* aRCID) {
702 if (!mRequestContext) {
703 return NS_ERROR_NOT_AVAILABLE;
705 *aRCID = mRequestContext->GetID();
706 return NS_OK;
709 ////////////////////////////////////////////////////////////////////////////////
710 // nsILoadGroupChild methods:
712 NS_IMETHODIMP
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);
718 return NS_OK;
721 NS_IMETHODIMP
722 nsLoadGroup::SetParentLoadGroup(nsILoadGroup* aParentLoadGroup) {
723 mParentLoadGroup = do_GetWeakReference(aParentLoadGroup);
724 return NS_OK;
727 NS_IMETHODIMP
728 nsLoadGroup::GetChildLoadGroup(nsILoadGroup** aChildLoadGroup) {
729 *aChildLoadGroup = do_AddRef(this).take();
730 return NS_OK;
733 NS_IMETHODIMP
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();
745 return NS_OK;
748 ////////////////////////////////////////////////////////////////////////////////
749 // nsISupportsPriority methods:
751 NS_IMETHODIMP
752 nsLoadGroup::GetPriority(int32_t* aValue) {
753 *aValue = mPriority;
754 return NS_OK;
757 NS_IMETHODIMP
758 nsLoadGroup::SetPriority(int32_t aValue) {
759 return AdjustPriority(aValue - mPriority);
762 NS_IMETHODIMP
763 nsLoadGroup::AdjustPriority(int32_t aDelta) {
764 // Update the priority for each request that supports nsISupportsPriority
765 if (aDelta != 0) {
766 mPriority += aDelta;
767 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
768 auto* e = static_cast<RequestMapEntry*>(iter.Get());
769 RescheduleRequest(e->mKey, aDelta);
772 return NS_OK;
775 NS_IMETHODIMP
776 nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags) {
777 *aFlags = mDefaultLoadFlags;
778 return NS_OK;
781 NS_IMETHODIMP
782 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags) {
783 mDefaultLoadFlags = aFlags;
784 return NS_OK;
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);
808 mTimedRequests = 0;
809 mCachedRequests = 0;
810 mDefaultLoadIsTimed = false;
813 void nsLoadGroup::TelemetryReportChannel(nsITimedChannel* aTimedChannel,
814 bool aDefaultRequest) {
815 nsresult rv;
816 bool timingEnabled;
817 rv = aTimedChannel->GetTimingEnabled(&timingEnabled);
818 if (NS_FAILED(rv) || !timingEnabled) return;
820 TimeStamp asyncOpen;
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);
869 if (httpChannel) {
870 uint32_t major;
871 uint32_t minor;
872 if (NS_SUCCEEDED(httpChannel->GetResponseVersion(&major, &minor))) {
873 useHttp3 = major == 3;
874 if (major == 2) {
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, \
901 connectEnd); \
904 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
905 Telemetry::AccumulateTimeDelta( \
906 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_SENT, asyncOpen, \
907 requestStart); \
909 Telemetry::AccumulateTimeDelta( \
910 Telemetry::HTTP_##prefix##_FIRST_SENT_TO_LAST_RECEIVED, requestStart, \
911 responseEnd); \
913 if (cacheReadStart.IsNull() && !responseStart.IsNull()) { \
914 Telemetry::AccumulateTimeDelta( \
915 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_RECEIVED, asyncOpen, \
916 responseStart); \
920 if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) { \
921 Telemetry::AccumulateTimeDelta( \
922 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_FROM_CACHE_V2, asyncOpen, \
923 cacheReadStart); \
925 Telemetry::AccumulateTimeDelta( \
926 Telemetry::HTTP_##prefix##_CACHE_READ_TIME_V2, cacheReadStart, \
927 cacheReadEnd); \
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, \
940 cacheReadEnd); \
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, \
946 responseEnd); \
949 if (aDefaultRequest) {
950 HTTP_REQUEST_HISTOGRAMS(PAGE)
951 } else {
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,
978 responseEnd);
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) {
1009 nsresult rv;
1010 nsLoadFlags flags, oldFlags;
1012 rv = aRequest->GetLoadFlags(&flags);
1013 if (NS_FAILED(rv)) {
1014 return rv;
1017 oldFlags = flags;
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);
1031 outFlags = flags;
1032 return rv;
1035 nsresult nsLoadGroup::MergeDefaultLoadFlags(nsIRequest* aRequest,
1036 nsLoadFlags& outFlags) {
1037 nsresult rv;
1038 nsLoadFlags flags, oldFlags;
1040 rv = aRequest->GetLoadFlags(&flags);
1041 if (NS_FAILED(rv)) {
1042 return rv;
1045 oldFlags = flags;
1046 // ... and force the default flags.
1047 flags |= mDefaultLoadFlags;
1049 if (flags != oldFlags) {
1050 rv = aRequest->SetLoadFlags(flags);
1052 outFlags = flags;
1053 return rv;
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);
1068 return NS_OK;
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);
1085 return NS_OK;
1088 NS_IMETHODIMP
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) {
1096 return NS_OK;
1099 mBrowsingContextDiscarded = true;
1100 return NS_OK;
1103 NS_IMETHODIMP
1104 nsLoadGroup::GetIsBrowsingContextDiscarded(bool* aIsBrowsingContextDiscarded) {
1105 *aIsBrowsingContextDiscarded = mBrowsingContextDiscarded;
1106 return NS_OK;
1109 } // namespace net
1110 } // namespace mozilla
1112 #undef LOG