Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / netwerk / base / nsLoadGroup.cpp
blobe2f7063235f788a4931e526da911ce873bd99dfe
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 "nsIHttpChannelInternal.h"
19 #include "nsITimedChannel.h"
20 #include "nsIInterfaceRequestor.h"
21 #include "nsIRequestObserver.h"
22 #include "CacheObserver.h"
23 #include "MainThreadUtils.h"
24 #include "RequestContextService.h"
25 #include "mozilla/glean/NetwerkMetrics.h"
26 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
27 #include "mozilla/StoragePrincipalHelper.h"
28 #include "mozilla/Unused.h"
29 #include "mozilla/net/NeckoCommon.h"
30 #include "mozilla/net/NeckoChild.h"
31 #include "mozilla/StaticPrefs_network.h"
33 namespace mozilla {
34 namespace net {
37 // Log module for nsILoadGroup logging...
39 // To enable logging (see prlog.h for full details):
41 // set MOZ_LOG=LoadGroup:5
42 // set MOZ_LOG_FILE=network.log
44 // This enables LogLevel::Debug level information and places all output in
45 // the file network.log.
47 static LazyLogModule gLoadGroupLog("LoadGroup");
48 #undef LOG
49 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args)
51 ////////////////////////////////////////////////////////////////////////////////
53 class RequestMapEntry : public PLDHashEntryHdr {
54 public:
55 explicit RequestMapEntry(nsIRequest* aRequest) : mKey(aRequest) {}
57 nsCOMPtr<nsIRequest> mKey;
60 static bool RequestHashMatchEntry(const PLDHashEntryHdr* entry,
61 const void* key) {
62 const RequestMapEntry* e = static_cast<const RequestMapEntry*>(entry);
63 const nsIRequest* request = static_cast<const nsIRequest*>(key);
65 return e->mKey == request;
68 static void RequestHashClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry) {
69 RequestMapEntry* e = static_cast<RequestMapEntry*>(entry);
71 // An entry is being cleared, let the entry do its own cleanup.
72 e->~RequestMapEntry();
75 static void RequestHashInitEntry(PLDHashEntryHdr* entry, const void* key) {
76 const nsIRequest* const_request = static_cast<const nsIRequest*>(key);
77 nsIRequest* request = const_cast<nsIRequest*>(const_request);
79 // Initialize the entry with placement new
80 new (entry) RequestMapEntry(request);
83 static const PLDHashTableOps sRequestHashOps = {
84 PLDHashTable::HashVoidPtrKeyStub, RequestHashMatchEntry,
85 PLDHashTable::MoveEntryStub, RequestHashClearEntry, RequestHashInitEntry};
87 static void RescheduleRequest(nsIRequest* aRequest, int32_t delta) {
88 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aRequest);
89 if (p) p->AdjustPriority(delta);
92 nsLoadGroup::nsLoadGroup()
93 : mRequests(&sRequestHashOps, sizeof(RequestMapEntry)) {
94 LOG(("LOADGROUP [%p]: Created.\n", this));
97 nsLoadGroup::~nsLoadGroup() {
98 DebugOnly<nsresult> rv =
99 CancelWithReason(NS_BINDING_ABORTED, "nsLoadGroup::~nsLoadGroup"_ns);
100 NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed");
102 mDefaultLoadRequest = nullptr;
104 if (mRequestContext && !mExternalRequestContext) {
105 mRequestContextService->RemoveRequestContext(mRequestContext->GetID());
106 if (IsNeckoChild() && gNeckoChild && gNeckoChild->CanSend()) {
107 gNeckoChild->SendRemoveRequestContext(mRequestContext->GetID());
111 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
112 if (os) {
113 Unused << os->RemoveObserver(this, "last-pb-context-exited");
116 LOG(("LOADGROUP [%p]: Destroyed.\n", this));
119 ////////////////////////////////////////////////////////////////////////////////
120 // nsISupports methods:
122 NS_IMPL_ISUPPORTS(nsLoadGroup, nsILoadGroup, nsILoadGroupChild, nsIRequest,
123 nsISupportsPriority, nsISupportsWeakReference, nsIObserver)
125 ////////////////////////////////////////////////////////////////////////////////
126 // nsIRequest methods:
128 NS_IMETHODIMP
129 nsLoadGroup::GetName(nsACString& result) {
130 // XXX is this the right "name" for a load group?
132 if (!mDefaultLoadRequest) {
133 result.Truncate();
134 return NS_OK;
137 return mDefaultLoadRequest->GetName(result);
140 NS_IMETHODIMP
141 nsLoadGroup::IsPending(bool* aResult) {
142 *aResult = mForegroundCount > 0;
143 return NS_OK;
146 NS_IMETHODIMP
147 nsLoadGroup::GetStatus(nsresult* status) {
148 if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest) {
149 return mDefaultLoadRequest->GetStatus(status);
152 *status = mStatus;
153 return NS_OK;
156 static bool AppendRequestsToArray(PLDHashTable* aTable,
157 nsTArray<nsIRequest*>* aArray) {
158 for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
159 auto* e = static_cast<RequestMapEntry*>(iter.Get());
160 nsIRequest* request = e->mKey;
161 MOZ_DIAGNOSTIC_ASSERT(request, "Null key in mRequests PLDHashTable entry");
163 // XXX(Bug 1631371) Check if this should use a fallible operation as it
164 // pretended earlier.
165 aArray->AppendElement(request);
166 NS_ADDREF(request);
169 if (aArray->Length() != aTable->EntryCount()) {
170 for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
171 NS_RELEASE((*aArray)[i]);
173 return false;
175 return true;
178 NS_IMETHODIMP nsLoadGroup::SetCanceledReason(const nsACString& aReason) {
179 return SetCanceledReasonImpl(aReason);
182 NS_IMETHODIMP nsLoadGroup::GetCanceledReason(nsACString& aReason) {
183 return GetCanceledReasonImpl(aReason);
186 NS_IMETHODIMP nsLoadGroup::CancelWithReason(nsresult aStatus,
187 const nsACString& aReason) {
188 return CancelWithReasonImpl(aStatus, aReason);
191 NS_IMETHODIMP
192 nsLoadGroup::Cancel(nsresult status) {
193 MOZ_ASSERT(NS_IsMainThread());
195 NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
196 nsresult rv;
197 uint32_t count = mRequests.EntryCount();
199 AutoTArray<nsIRequest*, 8> requests;
201 if (!AppendRequestsToArray(&mRequests, &requests)) {
202 return NS_ERROR_OUT_OF_MEMORY;
205 // set the load group status to our cancel status while we cancel
206 // all our requests...once the cancel is done, we'll reset it...
208 mStatus = status;
210 // Set the flag indicating that the loadgroup is being canceled... This
211 // prevents any new channels from being added during the operation.
213 mIsCanceling = true;
215 nsresult firstError = NS_OK;
216 while (count > 0) {
217 nsCOMPtr<nsIRequest> request = requests.ElementAt(--count);
219 NS_ASSERTION(request, "NULL request found in list.");
221 if (!mRequests.Search(request)) {
222 // |request| was removed already
223 // We need to null out the entry in the request array so we don't try
224 // to notify the observers for this request.
225 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(count));
226 requests.ElementAt(count) = nullptr;
228 continue;
231 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
232 nsAutoCString nameStr;
233 request->GetName(nameStr);
234 LOG(("LOADGROUP [%p]: Canceling request %p %s.\n", this, request.get(),
235 nameStr.get()));
238 // Cancel the request...
239 rv = request->CancelWithReason(status, mCanceledReason);
241 // Remember the first failure and return it...
242 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv;
244 if (NS_FAILED(RemoveRequestFromHashtable(request, status))) {
245 // It's possible that request->Cancel causes the request to be removed
246 // from the loadgroup causing RemoveRequestFromHashtable to fail.
247 // In that case we shouldn't call NotifyRemovalObservers or decrement
248 // mForegroundCount since that has already happened.
249 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(count));
250 requests.ElementAt(count) = nullptr;
252 continue;
256 for (count = requests.Length(); count > 0;) {
257 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count));
258 (void)NotifyRemovalObservers(request, status);
261 if (mRequestContext) {
262 Unused << mRequestContext->CancelTailPendingRequests(status);
265 #if defined(DEBUG)
266 NS_ASSERTION(mRequests.EntryCount() == 0, "Request list is not empty.");
267 NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");
268 #endif
270 mStatus = NS_OK;
271 mIsCanceling = false;
273 return firstError;
276 NS_IMETHODIMP
277 nsLoadGroup::Suspend() {
278 nsresult rv, firstError;
279 uint32_t count = mRequests.EntryCount();
281 AutoTArray<nsIRequest*, 8> requests;
283 if (!AppendRequestsToArray(&mRequests, &requests)) {
284 return NS_ERROR_OUT_OF_MEMORY;
287 firstError = NS_OK;
289 // Operate the elements from back to front so that if items get
290 // get removed from the list it won't affect our iteration
292 while (count > 0) {
293 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count));
295 NS_ASSERTION(request, "NULL request found in list.");
296 if (!request) continue;
298 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
299 nsAutoCString nameStr;
300 request->GetName(nameStr);
301 LOG(("LOADGROUP [%p]: Suspending request %p %s.\n", this, request.get(),
302 nameStr.get()));
305 // Suspend the request...
306 rv = request->Suspend();
308 // Remember the first failure and return it...
309 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv;
312 return firstError;
315 NS_IMETHODIMP
316 nsLoadGroup::Resume() {
317 nsresult rv, firstError;
318 uint32_t count = mRequests.EntryCount();
320 AutoTArray<nsIRequest*, 8> requests;
322 if (!AppendRequestsToArray(&mRequests, &requests)) {
323 return NS_ERROR_OUT_OF_MEMORY;
326 firstError = NS_OK;
328 // Operate the elements from back to front so that if items get
329 // get removed from the list it won't affect our iteration
331 while (count > 0) {
332 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count));
334 NS_ASSERTION(request, "NULL request found in list.");
335 if (!request) continue;
337 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) {
338 nsAutoCString nameStr;
339 request->GetName(nameStr);
340 LOG(("LOADGROUP [%p]: Resuming request %p %s.\n", this, request.get(),
341 nameStr.get()));
344 // Resume the request...
345 rv = request->Resume();
347 // Remember the first failure and return it...
348 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv;
351 return firstError;
354 NS_IMETHODIMP
355 nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags) {
356 *aLoadFlags = mLoadFlags;
357 return NS_OK;
360 NS_IMETHODIMP
361 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags) {
362 mLoadFlags = aLoadFlags;
363 return NS_OK;
366 NS_IMETHODIMP
367 nsLoadGroup::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
368 return GetTRRModeImpl(aTRRMode);
371 NS_IMETHODIMP
372 nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
373 return SetTRRModeImpl(aTRRMode);
376 NS_IMETHODIMP
377 nsLoadGroup::GetLoadGroup(nsILoadGroup** loadGroup) {
378 nsCOMPtr<nsILoadGroup> result = mLoadGroup;
379 result.forget(loadGroup);
380 return NS_OK;
383 NS_IMETHODIMP
384 nsLoadGroup::SetLoadGroup(nsILoadGroup* loadGroup) {
385 mLoadGroup = loadGroup;
386 return NS_OK;
389 ////////////////////////////////////////////////////////////////////////////////
390 // nsILoadGroup methods:
392 NS_IMETHODIMP
393 nsLoadGroup::GetDefaultLoadRequest(nsIRequest** aRequest) {
394 nsCOMPtr<nsIRequest> result = mDefaultLoadRequest;
395 result.forget(aRequest);
396 return NS_OK;
399 NS_IMETHODIMP
400 nsLoadGroup::SetDefaultLoadRequest(nsIRequest* aRequest) {
401 LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this,
402 aRequest));
404 mDefaultLoadRequest = aRequest;
405 // Inherit the group load flags from the default load request
406 if (mDefaultLoadRequest) {
407 mDefaultLoadRequest->GetLoadFlags(&mLoadFlags);
409 // Mask off any bits that are not part of the nsIRequest flags.
410 // in particular, nsIChannel::LOAD_DOCUMENT_URI...
412 mLoadFlags &= nsIRequest::LOAD_REQUESTMASK;
414 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(aRequest);
415 mDefaultLoadIsTimed = timedChannel != nullptr;
416 if (mDefaultLoadIsTimed) {
417 timedChannel->GetChannelCreation(&mDefaultRequestCreationTime);
420 // Else, do not change the group's load flags (see bug 95981)
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 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 // Cache the status of mDefaultLoadRequest, It'll be used later in
566 // TelemetryReport.
567 if (request == mDefaultLoadRequest) {
568 mDefaultStatus = aStatus;
571 // Collect telemetry stats only when default request is a timed channel.
572 // Don't include failed requests in the timing statistics.
573 if (mDefaultLoadIsTimed && NS_SUCCEEDED(aStatus)) {
574 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(request);
575 if (timedChannel) {
576 // Figure out if this request was served from the cache
577 ++mTimedRequests;
578 TimeStamp timeStamp;
579 rv = timedChannel->GetCacheReadStart(&timeStamp);
580 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
581 ++mCachedRequests;
584 if (request == mDefaultLoadRequest) {
585 TelemetryReportChannel(timedChannel, true);
586 } else {
587 rv = timedChannel->GetAsyncOpen(&timeStamp);
588 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
589 glean::http::subitem_open_latency_time.AccumulateRawDuration(
590 timeStamp - mDefaultRequestCreationTime);
593 rv = timedChannel->GetResponseStart(&timeStamp);
594 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
595 glean::http::subitem_first_byte_latency_time.AccumulateRawDuration(
596 timeStamp - mDefaultRequestCreationTime);
599 TelemetryReportChannel(timedChannel, false);
604 if (mRequests.EntryCount() == 0) {
605 TelemetryReport();
608 return NS_OK;
611 nsresult nsLoadGroup::NotifyRemovalObservers(nsIRequest* request,
612 nsresult aStatus) {
613 NS_ENSURE_ARG_POINTER(request);
614 // Undo any group priority delta...
615 if (mPriority != 0) RescheduleRequest(request, -mPriority);
617 nsLoadFlags flags;
618 nsresult rv = request->GetLoadFlags(&flags);
619 if (NS_FAILED(rv)) return rv;
621 bool foreground = !(flags & nsIRequest::LOAD_BACKGROUND);
622 if (foreground) {
623 NS_ASSERTION(mForegroundCount > 0, "ForegroundCount messed up");
624 mForegroundCount -= 1;
627 if (foreground || mNotifyObserverAboutBackgroundRequests) {
628 // Fire the OnStopRequest out to the observer...
629 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
630 if (observer) {
631 LOG(
632 ("LOADGROUP [%p]: Firing OnStopRequest for request %p."
633 "(foreground count=%d).\n",
634 this, request, mForegroundCount));
636 rv = observer->OnStopRequest(request, aStatus);
638 if (NS_FAILED(rv)) {
639 LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this,
640 request));
644 // If that was the last request -> remove ourselves from loadgroup
645 if (foreground && mForegroundCount == 0 && mLoadGroup) {
646 mLoadGroup->RemoveRequest(this, nullptr, aStatus);
650 return rv;
653 NS_IMETHODIMP
654 nsLoadGroup::GetRequests(nsISimpleEnumerator** aRequests) {
655 nsCOMArray<nsIRequest> requests;
656 requests.SetCapacity(mRequests.EntryCount());
658 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
659 auto* e = static_cast<RequestMapEntry*>(iter.Get());
660 requests.AppendObject(e->mKey);
663 return NS_NewArrayEnumerator(aRequests, requests, NS_GET_IID(nsIRequest));
666 NS_IMETHODIMP
667 nsLoadGroup::GetTotalKeepAliveBytes(uint64_t* aTotalKeepAliveBytes) {
668 MOZ_ASSERT(aTotalKeepAliveBytes);
669 *aTotalKeepAliveBytes = mPendingKeepaliveRequestSize;
670 return NS_OK;
673 NS_IMETHODIMP
674 nsLoadGroup::SetTotalKeepAliveBytes(uint64_t aTotalKeepAliveBytes) {
675 mPendingKeepaliveRequestSize = aTotalKeepAliveBytes;
676 return NS_OK;
679 NS_IMETHODIMP
680 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) {
681 SetGroupObserver(aObserver, false);
682 return NS_OK;
685 void nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver,
686 bool aIncludeBackgroundRequests) {
687 mObserver = do_GetWeakReference(aObserver);
688 mNotifyObserverAboutBackgroundRequests = aIncludeBackgroundRequests;
691 NS_IMETHODIMP
692 nsLoadGroup::GetGroupObserver(nsIRequestObserver** aResult) {
693 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
694 observer.forget(aResult);
695 return NS_OK;
698 NS_IMETHODIMP
699 nsLoadGroup::GetActiveCount(uint32_t* aResult) {
700 *aResult = mForegroundCount;
701 return NS_OK;
704 NS_IMETHODIMP
705 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
706 NS_ENSURE_ARG_POINTER(aCallbacks);
707 nsCOMPtr<nsIInterfaceRequestor> callbacks = mCallbacks;
708 callbacks.forget(aCallbacks);
709 return NS_OK;
712 NS_IMETHODIMP
713 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
714 mCallbacks = aCallbacks;
715 return NS_OK;
718 NS_IMETHODIMP
719 nsLoadGroup::GetRequestContextID(uint64_t* aRCID) {
720 if (!mRequestContext) {
721 return NS_ERROR_NOT_AVAILABLE;
723 *aRCID = mRequestContext->GetID();
724 return NS_OK;
727 ////////////////////////////////////////////////////////////////////////////////
728 // nsILoadGroupChild methods:
730 NS_IMETHODIMP
731 nsLoadGroup::GetParentLoadGroup(nsILoadGroup** aParentLoadGroup) {
732 *aParentLoadGroup = nullptr;
733 nsCOMPtr<nsILoadGroup> parent = do_QueryReferent(mParentLoadGroup);
734 if (!parent) return NS_OK;
735 parent.forget(aParentLoadGroup);
736 return NS_OK;
739 NS_IMETHODIMP
740 nsLoadGroup::SetParentLoadGroup(nsILoadGroup* aParentLoadGroup) {
741 mParentLoadGroup = do_GetWeakReference(aParentLoadGroup);
742 return NS_OK;
745 NS_IMETHODIMP
746 nsLoadGroup::GetChildLoadGroup(nsILoadGroup** aChildLoadGroup) {
747 *aChildLoadGroup = do_AddRef(this).take();
748 return NS_OK;
751 NS_IMETHODIMP
752 nsLoadGroup::GetRootLoadGroup(nsILoadGroup** aRootLoadGroup) {
753 // first recursively try the root load group of our parent
754 nsCOMPtr<nsILoadGroupChild> ancestor = do_QueryReferent(mParentLoadGroup);
755 if (ancestor) return ancestor->GetRootLoadGroup(aRootLoadGroup);
757 // next recursively try the root load group of our own load grop
758 ancestor = do_QueryInterface(mLoadGroup);
759 if (ancestor) return ancestor->GetRootLoadGroup(aRootLoadGroup);
761 // finally just return this
762 *aRootLoadGroup = do_AddRef(this).take();
763 return NS_OK;
766 ////////////////////////////////////////////////////////////////////////////////
767 // nsISupportsPriority methods:
769 NS_IMETHODIMP
770 nsLoadGroup::GetPriority(int32_t* aValue) {
771 *aValue = mPriority;
772 return NS_OK;
775 NS_IMETHODIMP
776 nsLoadGroup::SetPriority(int32_t aValue) {
777 return AdjustPriority(aValue - mPriority);
780 NS_IMETHODIMP
781 nsLoadGroup::AdjustPriority(int32_t aDelta) {
782 // Update the priority for each request that supports nsISupportsPriority
783 if (aDelta != 0) {
784 mPriority += aDelta;
785 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
786 auto* e = static_cast<RequestMapEntry*>(iter.Get());
787 RescheduleRequest(e->mKey, aDelta);
790 return NS_OK;
793 NS_IMETHODIMP
794 nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags) {
795 *aFlags = mDefaultLoadFlags;
796 return NS_OK;
799 NS_IMETHODIMP
800 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags) {
801 mDefaultLoadFlags = aFlags;
802 return NS_OK;
805 ////////////////////////////////////////////////////////////////////////////////
807 void nsLoadGroup::TelemetryReport() {
808 // We should only report HTTP_PAGE_* telemetry if the defaultRequest was
809 // actually successful.
810 if (mDefaultLoadIsTimed && NS_SUCCEEDED(mDefaultStatus)) {
811 glean::http::request_per_page.AccumulateSingleSample(mTimedRequests);
812 if (mTimedRequests) {
813 glean::http::request_per_page_from_cache.AccumulateSingleSample(
814 mCachedRequests * 100 / mTimedRequests);
818 mTimedRequests = 0;
819 mCachedRequests = 0;
820 mDefaultLoadIsTimed = false;
823 void nsLoadGroup::TelemetryReportChannel(nsITimedChannel* aTimedChannel,
824 bool aDefaultRequest) {
825 nsresult rv;
827 TimeStamp asyncOpen;
828 rv = aTimedChannel->GetAsyncOpen(&asyncOpen);
829 // We do not check !asyncOpen.IsNull() bellow, prevent ASSERTIONs this way
830 if (NS_FAILED(rv) || asyncOpen.IsNull()) return;
832 TimeStamp cacheReadStart;
833 rv = aTimedChannel->GetCacheReadStart(&cacheReadStart);
834 if (NS_FAILED(rv)) return;
836 TimeStamp cacheReadEnd;
837 rv = aTimedChannel->GetCacheReadEnd(&cacheReadEnd);
838 if (NS_FAILED(rv)) return;
840 TimeStamp domainLookupStart;
841 rv = aTimedChannel->GetDomainLookupStart(&domainLookupStart);
842 if (NS_FAILED(rv)) return;
844 TimeStamp domainLookupEnd;
845 rv = aTimedChannel->GetDomainLookupEnd(&domainLookupEnd);
846 if (NS_FAILED(rv)) return;
848 TimeStamp connectStart;
849 rv = aTimedChannel->GetConnectStart(&connectStart);
850 if (NS_FAILED(rv)) return;
852 TimeStamp secureConnectionStart;
853 rv = aTimedChannel->GetSecureConnectionStart(&secureConnectionStart);
854 if (NS_FAILED(rv)) return;
856 TimeStamp connectEnd;
857 rv = aTimedChannel->GetConnectEnd(&connectEnd);
858 if (NS_FAILED(rv)) return;
860 TimeStamp requestStart;
861 rv = aTimedChannel->GetRequestStart(&requestStart);
862 if (NS_FAILED(rv)) return;
864 TimeStamp responseStart;
865 rv = aTimedChannel->GetResponseStart(&responseStart);
866 if (NS_FAILED(rv)) return;
868 TimeStamp responseEnd;
869 rv = aTimedChannel->GetResponseEnd(&responseEnd);
870 if (NS_FAILED(rv)) return;
871 #ifndef ANDROID
872 bool useHttp3 = false;
873 #endif
874 bool supportHttp3 = false;
875 nsCOMPtr<nsIHttpChannelInternal> httpChannel =
876 do_QueryInterface(aTimedChannel);
877 if (httpChannel) {
878 uint32_t major;
879 uint32_t minor;
880 if (NS_SUCCEEDED(httpChannel->GetResponseVersion(&major, &minor))) {
881 #ifndef ANDROID
882 useHttp3 = major == 3;
883 #endif
884 if (major == 2) {
885 if (NS_FAILED(httpChannel->GetSupportsHTTP3(&supportHttp3))) {
886 supportHttp3 = false;
892 // Glean instrumentation of metrics previously collected via Geckoview
893 // Streaming.
894 if (!domainLookupStart.IsNull()) {
895 if (aDefaultRequest) {
896 mozilla::glean::network::dns_start.AccumulateRawDuration(
897 domainLookupStart - asyncOpen);
898 if (!domainLookupEnd.IsNull()) {
899 mozilla::glean::network::dns_end.AccumulateRawDuration(
900 domainLookupEnd - domainLookupStart);
903 #ifndef ANDROID
904 else {
905 mozilla::glean::network::sub_dns_start.AccumulateRawDuration(
906 domainLookupStart - asyncOpen);
907 if (!domainLookupEnd.IsNull()) {
908 mozilla::glean::network::sub_dns_end.AccumulateRawDuration(
909 domainLookupEnd - domainLookupStart);
912 #endif
914 if (!connectEnd.IsNull()) {
915 if (!connectStart.IsNull()) {
916 if (aDefaultRequest) {
917 mozilla::glean::network::tcp_connection.AccumulateRawDuration(
918 connectEnd - connectStart);
920 #ifndef ANDROID
921 else {
922 mozilla::glean::network::sub_tcp_connection.AccumulateRawDuration(
923 connectEnd - connectStart);
925 #endif
927 if (!secureConnectionStart.IsNull()) {
928 if (aDefaultRequest) {
929 mozilla::glean::network::tls_handshake.AccumulateRawDuration(
930 connectEnd - secureConnectionStart);
932 #ifndef ANDROID
933 else {
934 mozilla::glean::network::sub_tls_handshake.AccumulateRawDuration(
935 connectEnd - secureConnectionStart);
937 #endif
940 if (!requestStart.IsNull() && !responseEnd.IsNull()) {
941 if (aDefaultRequest) {
942 mozilla::glean::network::open_to_first_sent.AccumulateRawDuration(
943 requestStart - asyncOpen);
944 mozilla::glean::network::first_sent_to_last_received
945 .AccumulateRawDuration(responseEnd - requestStart);
947 if (cacheReadStart.IsNull() && !responseStart.IsNull()) {
948 mozilla::glean::network::open_to_first_received.AccumulateRawDuration(
949 responseStart - asyncOpen);
952 #ifndef ANDROID
953 else {
954 mozilla::glean::network::sub_open_to_first_sent.AccumulateRawDuration(
955 requestStart - asyncOpen);
956 mozilla::glean::network::sub_first_sent_to_last_received
957 .AccumulateRawDuration(responseEnd - requestStart);
958 if (cacheReadStart.IsNull() && !responseStart.IsNull()) {
959 mozilla::glean::network::sub_open_to_first_received
960 .AccumulateRawDuration(responseStart - asyncOpen);
963 #endif
965 if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) {
966 if (aDefaultRequest) {
967 mozilla::glean::network::first_from_cache.AccumulateRawDuration(
968 cacheReadStart - asyncOpen);
969 #ifndef ANDROID
970 mozilla::glean::network::cache_read_time.AccumulateRawDuration(
971 cacheReadEnd - cacheReadStart);
972 if (!requestStart.IsNull() && !responseEnd.IsNull()) {
973 mozilla::glean::network::http_revalidation.AccumulateRawDuration(
974 responseEnd - requestStart);
976 #endif
978 #ifndef ANDROID
979 else {
980 mozilla::glean::network::sub_first_from_cache.AccumulateRawDuration(
981 cacheReadStart - asyncOpen);
982 mozilla::glean::network::sub_cache_read_time.AccumulateRawDuration(
983 cacheReadEnd - cacheReadStart);
984 if (!requestStart.IsNull() && !responseEnd.IsNull()) {
985 mozilla::glean::network::sub_http_revalidation.AccumulateRawDuration(
986 responseEnd - requestStart);
989 #endif
991 #ifndef ANDROID
992 if (!cacheReadEnd.IsNull()) {
993 if (aDefaultRequest) {
994 mozilla::glean::network::complete_load.AccumulateRawDuration(
995 cacheReadEnd - asyncOpen);
996 mozilla::glean::network::complete_load_cached.AccumulateRawDuration(
997 cacheReadEnd - asyncOpen);
998 } else {
999 mozilla::glean::network::sub_complete_load.AccumulateRawDuration(
1000 cacheReadEnd - asyncOpen);
1001 mozilla::glean::network::sub_complete_load_cached.AccumulateRawDuration(
1002 cacheReadEnd - asyncOpen);
1004 } else if (!responseEnd.IsNull()) {
1005 if (aDefaultRequest) {
1006 mozilla::glean::network::complete_load.AccumulateRawDuration(responseEnd -
1007 asyncOpen);
1008 mozilla::glean::network::complete_load_net.AccumulateRawDuration(
1009 responseEnd - asyncOpen);
1010 } else {
1011 mozilla::glean::network::sub_complete_load.AccumulateRawDuration(
1012 responseEnd - asyncOpen);
1013 mozilla::glean::network::sub_complete_load_net.AccumulateRawDuration(
1014 responseEnd - asyncOpen);
1017 #endif
1019 #ifndef ANDROID
1020 if ((useHttp3 || supportHttp3) && cacheReadStart.IsNull() &&
1021 cacheReadEnd.IsNull()) {
1022 nsCString key = (useHttp3) ? ((aDefaultRequest) ? "uses_http3_page"_ns
1023 : "uses_http3_sub"_ns)
1024 : ((aDefaultRequest) ? "supports_http3_page"_ns
1025 : "supports_http3_sub"_ns);
1027 if (!secureConnectionStart.IsNull() && !connectEnd.IsNull()) {
1028 mozilla::glean::network::http3_tls_handshake.Get(key)
1029 .AccumulateRawDuration(connectEnd - secureConnectionStart);
1032 if (supportHttp3 && !connectStart.IsNull() && !connectEnd.IsNull()) {
1033 mozilla::glean::network::sup_http3_tcp_connection.Get(key)
1034 .AccumulateRawDuration(connectEnd - connectStart);
1037 if (!requestStart.IsNull() && !responseEnd.IsNull()) {
1038 mozilla::glean::network::http3_open_to_first_sent.Get(key)
1039 .AccumulateRawDuration(requestStart - asyncOpen);
1041 mozilla::glean::network::http3_first_sent_to_last_received.Get(key)
1042 .AccumulateRawDuration(responseEnd - requestStart);
1044 if (!responseStart.IsNull()) {
1045 mozilla::glean::network::http3_open_to_first_received.Get(key)
1046 .AccumulateRawDuration(responseStart - asyncOpen);
1049 if (!responseEnd.IsNull()) {
1050 mozilla::glean::network::http3_complete_load.Get(key)
1051 .AccumulateRawDuration(responseEnd - asyncOpen);
1055 #endif
1057 bool hasHTTPSRR = false;
1058 if (httpChannel && NS_SUCCEEDED(httpChannel->GetHasHTTPSRR(&hasHTTPSRR)) &&
1059 cacheReadStart.IsNull() && cacheReadEnd.IsNull() &&
1060 !requestStart.IsNull()) {
1061 TimeDuration elapsed = requestStart - asyncOpen;
1062 if (hasHTTPSRR) {
1063 if (aDefaultRequest) {
1064 glean::networking::http_channel_page_open_to_first_sent_https_rr
1065 .AccumulateRawDuration(elapsed);
1066 } else {
1067 glean::networking::http_channel_sub_open_to_first_sent_https_rr
1068 .AccumulateRawDuration(elapsed);
1070 } else {
1071 if (aDefaultRequest) {
1072 glean::networking::http_channel_page_open_to_first_sent
1073 .AccumulateRawDuration(elapsed);
1074 } else {
1075 glean::networking::http_channel_sub_open_to_first_sent
1076 .AccumulateRawDuration(elapsed);
1082 nsresult nsLoadGroup::MergeLoadFlags(nsIRequest* aRequest,
1083 nsLoadFlags& outFlags) {
1084 nsresult rv;
1085 nsLoadFlags flags, oldFlags;
1087 rv = aRequest->GetLoadFlags(&flags);
1088 if (NS_FAILED(rv)) {
1089 return rv;
1092 oldFlags = flags;
1094 // Inherit some bits...
1095 flags |= mLoadFlags & kInheritedLoadFlags;
1097 // ... and force the default flags.
1098 flags |= mDefaultLoadFlags;
1100 if (flags != oldFlags) {
1101 rv = aRequest->SetLoadFlags(flags);
1104 outFlags = flags;
1105 return rv;
1108 nsresult nsLoadGroup::MergeDefaultLoadFlags(nsIRequest* aRequest,
1109 nsLoadFlags& outFlags) {
1110 nsresult rv;
1111 nsLoadFlags flags, oldFlags;
1113 rv = aRequest->GetLoadFlags(&flags);
1114 if (NS_FAILED(rv)) {
1115 return rv;
1118 oldFlags = flags;
1119 // ... and force the default flags.
1120 flags |= mDefaultLoadFlags;
1122 if (flags != oldFlags) {
1123 rv = aRequest->SetLoadFlags(flags);
1125 outFlags = flags;
1126 return rv;
1129 nsresult nsLoadGroup::Init() {
1130 mRequestContextService = RequestContextService::GetOrCreate();
1131 if (mRequestContextService) {
1132 Unused << mRequestContextService->NewRequestContext(
1133 getter_AddRefs(mRequestContext));
1136 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1137 NS_ENSURE_STATE(os);
1139 Unused << os->AddObserver(this, "last-pb-context-exited", true);
1141 return NS_OK;
1144 nsresult nsLoadGroup::InitWithRequestContextId(
1145 const uint64_t& aRequestContextId) {
1146 mRequestContextService = RequestContextService::GetOrCreate();
1147 if (mRequestContextService) {
1148 Unused << mRequestContextService->GetRequestContext(
1149 aRequestContextId, getter_AddRefs(mRequestContext));
1151 mExternalRequestContext = true;
1153 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1154 NS_ENSURE_STATE(os);
1156 Unused << os->AddObserver(this, "last-pb-context-exited", true);
1158 return NS_OK;
1161 NS_IMETHODIMP
1162 nsLoadGroup::Observe(nsISupports* aSubject, const char* aTopic,
1163 const char16_t* aData) {
1164 MOZ_ASSERT(!strcmp(aTopic, "last-pb-context-exited"));
1166 OriginAttributes attrs;
1167 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs);
1168 if (!attrs.IsPrivateBrowsing()) {
1169 return NS_OK;
1172 mBrowsingContextDiscarded = true;
1173 return NS_OK;
1176 NS_IMETHODIMP
1177 nsLoadGroup::GetIsBrowsingContextDiscarded(bool* aIsBrowsingContextDiscarded) {
1178 *aIsBrowsingContextDiscarded = mBrowsingContextDiscarded;
1179 return NS_OK;
1182 } // namespace net
1183 } // namespace mozilla
1185 #undef LOG