nsLoadGroup.cpp (36178B)
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/. */ 6 7 #include "mozilla/DebugOnly.h" 8 9 #include "nsLoadGroup.h" 10 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 "nsIHttpChannel.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/glean/NetwerkMetrics.h" 27 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 28 #include "mozilla/StoragePrincipalHelper.h" 29 #include "mozilla/net/NeckoCommon.h" 30 #include "mozilla/net/NeckoChild.h" 31 #include "mozilla/StaticPrefs_network.h" 32 33 namespace mozilla { 34 namespace net { 35 36 // 37 // Log module for nsILoadGroup logging... 38 // 39 // To enable logging (see prlog.h for full details): 40 // 41 // set MOZ_LOG=LoadGroup:5 42 // set MOZ_LOG_FILE=network.log 43 // 44 // This enables LogLevel::Debug level information and places all output in 45 // the file network.log. 46 // 47 static LazyLogModule gLoadGroupLog("LoadGroup"); 48 #undef LOG 49 #define LOG(args) MOZ_LOG(gLoadGroupLog, mozilla::LogLevel::Debug, args) 50 51 //////////////////////////////////////////////////////////////////////////////// 52 53 class RequestMapEntry : public PLDHashEntryHdr { 54 public: 55 explicit RequestMapEntry(nsIRequest* aRequest) : mKey(aRequest) {} 56 57 nsCOMPtr<nsIRequest> mKey; 58 }; 59 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); 64 65 return e->mKey == request; 66 } 67 68 static void RequestHashClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry) { 69 RequestMapEntry* e = static_cast<RequestMapEntry*>(entry); 70 71 // An entry is being cleared, let the entry do its own cleanup. 72 e->~RequestMapEntry(); 73 } 74 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); 78 79 // Initialize the entry with placement new 80 new (entry) RequestMapEntry(request); 81 } 82 83 static const PLDHashTableOps sRequestHashOps = { 84 PLDHashTable::HashVoidPtrKeyStub, RequestHashMatchEntry, 85 PLDHashTable::MoveEntryStub, RequestHashClearEntry, RequestHashInitEntry}; 86 87 static void RescheduleRequest(nsIRequest* aRequest, int32_t delta) { 88 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aRequest); 89 if (p) p->AdjustPriority(delta); 90 } 91 92 nsLoadGroup::nsLoadGroup() 93 : mRequests(&sRequestHashOps, sizeof(RequestMapEntry)) { 94 LOG(("LOADGROUP [%p]: Created.\n", this)); 95 } 96 97 nsLoadGroup::~nsLoadGroup() { 98 DebugOnly<nsresult> rv = 99 CancelWithReason(NS_BINDING_ABORTED, "nsLoadGroup::~nsLoadGroup"_ns); 100 NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed"); 101 102 mDefaultLoadRequest = nullptr; 103 104 if (mRequestContext && !mExternalRequestContext) { 105 mRequestContextService->RemoveRequestContext(mRequestContext->GetID()); 106 if (IsNeckoChild() && gNeckoChild && gNeckoChild->CanSend()) { 107 gNeckoChild->SendRemoveRequestContext(mRequestContext->GetID()); 108 } 109 } 110 111 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 112 if (os) { 113 (void)os->RemoveObserver(this, "last-pb-context-exited"); 114 } 115 116 if (mPageSize) { 117 glean::network::page_load_size.Get("page"_ns).Accumulate(mPageSize); 118 } 119 if (mTotalSubresourcesSize) { 120 glean::network::page_load_size.Get("subresources"_ns) 121 .Accumulate(mTotalSubresourcesSize); 122 } 123 124 LOG(("LOADGROUP [%p]: Destroyed.\n", this)); 125 } 126 127 //////////////////////////////////////////////////////////////////////////////// 128 // nsISupports methods: 129 130 NS_IMPL_ISUPPORTS(nsLoadGroup, nsILoadGroup, nsILoadGroupChild, nsIRequest, 131 nsISupportsPriority, nsISupportsWeakReference, nsIObserver) 132 133 //////////////////////////////////////////////////////////////////////////////// 134 // nsIRequest methods: 135 136 NS_IMETHODIMP 137 nsLoadGroup::GetName(nsACString& result) { 138 // XXX is this the right "name" for a load group? 139 140 if (!mDefaultLoadRequest) { 141 result.Truncate(); 142 return NS_OK; 143 } 144 145 return mDefaultLoadRequest->GetName(result); 146 } 147 148 NS_IMETHODIMP 149 nsLoadGroup::IsPending(bool* aResult) { 150 *aResult = mForegroundCount > 0; 151 return NS_OK; 152 } 153 154 NS_IMETHODIMP 155 nsLoadGroup::GetStatus(nsresult* status) { 156 if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest) { 157 return mDefaultLoadRequest->GetStatus(status); 158 } 159 160 *status = mStatus; 161 return NS_OK; 162 } 163 164 static bool AppendRequestsToArray(PLDHashTable* aTable, 165 nsTArray<nsIRequest*>* aArray) { 166 for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) { 167 auto* e = static_cast<RequestMapEntry*>(iter.Get()); 168 nsIRequest* request = e->mKey; 169 MOZ_DIAGNOSTIC_ASSERT(request, "Null key in mRequests PLDHashTable entry"); 170 171 // XXX(Bug 1631371) Check if this should use a fallible operation as it 172 // pretended earlier. 173 aArray->AppendElement(request); 174 NS_ADDREF(request); 175 } 176 177 if (aArray->Length() != aTable->EntryCount()) { 178 for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) { 179 NS_RELEASE((*aArray)[i]); 180 } 181 return false; 182 } 183 return true; 184 } 185 186 NS_IMETHODIMP nsLoadGroup::SetCanceledReason(const nsACString& aReason) { 187 return SetCanceledReasonImpl(aReason); 188 } 189 190 NS_IMETHODIMP nsLoadGroup::GetCanceledReason(nsACString& aReason) { 191 return GetCanceledReasonImpl(aReason); 192 } 193 194 NS_IMETHODIMP nsLoadGroup::CancelWithReason(nsresult aStatus, 195 const nsACString& aReason) { 196 return CancelWithReasonImpl(aStatus, aReason); 197 } 198 199 NS_IMETHODIMP 200 nsLoadGroup::Cancel(nsresult status) { 201 MOZ_ASSERT(NS_IsMainThread()); 202 203 NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); 204 nsresult rv; 205 uint32_t count = mRequests.EntryCount(); 206 207 AutoTArray<nsIRequest*, 8> requests; 208 209 if (!AppendRequestsToArray(&mRequests, &requests)) { 210 return NS_ERROR_OUT_OF_MEMORY; 211 } 212 213 // set the load group status to our cancel status while we cancel 214 // all our requests...once the cancel is done, we'll reset it... 215 // 216 mStatus = status; 217 218 // Set the flag indicating that the loadgroup is being canceled... This 219 // prevents any new channels from being added during the operation. 220 // 221 mIsCanceling = true; 222 223 nsresult firstError = NS_OK; 224 while (count > 0) { 225 nsCOMPtr<nsIRequest> request = requests.ElementAt(--count); 226 227 NS_ASSERTION(request, "NULL request found in list."); 228 229 if (!mRequests.Search(request)) { 230 // |request| was removed already 231 // We need to null out the entry in the request array so we don't try 232 // to notify the observers for this request. 233 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(count)); 234 requests.ElementAt(count) = nullptr; 235 236 continue; 237 } 238 239 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) { 240 nsAutoCString nameStr; 241 request->GetName(nameStr); 242 LOG(("LOADGROUP [%p]: Canceling request %p %s.\n", this, request.get(), 243 nameStr.get())); 244 } 245 246 // Cancel the request... 247 rv = request->CancelWithReason(status, mCanceledReason); 248 249 // Remember the first failure and return it... 250 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv; 251 252 if (NS_FAILED(RemoveRequestFromHashtable(request, status))) { 253 // It's possible that request->Cancel causes the request to be removed 254 // from the loadgroup causing RemoveRequestFromHashtable to fail. 255 // In that case we shouldn't call NotifyRemovalObservers or decrement 256 // mForegroundCount since that has already happened. 257 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(count)); 258 requests.ElementAt(count) = nullptr; 259 260 continue; 261 } 262 } 263 264 for (count = requests.Length(); count > 0;) { 265 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count)); 266 (void)NotifyRemovalObservers(request, status); 267 } 268 269 if (mRequestContext) { 270 (void)mRequestContext->CancelTailPendingRequests(status); 271 } 272 273 #if defined(DEBUG) 274 NS_ASSERTION(mRequests.EntryCount() == 0, "Request list is not empty."); 275 NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active."); 276 #endif 277 278 mStatus = NS_OK; 279 mIsCanceling = false; 280 281 return firstError; 282 } 283 284 NS_IMETHODIMP 285 nsLoadGroup::Suspend() { 286 nsresult rv, firstError; 287 uint32_t count = mRequests.EntryCount(); 288 289 AutoTArray<nsIRequest*, 8> requests; 290 291 if (!AppendRequestsToArray(&mRequests, &requests)) { 292 return NS_ERROR_OUT_OF_MEMORY; 293 } 294 295 firstError = NS_OK; 296 // 297 // Operate the elements from back to front so that if items get 298 // get removed from the list it won't affect our iteration 299 // 300 while (count > 0) { 301 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count)); 302 303 NS_ASSERTION(request, "NULL request found in list."); 304 if (!request) continue; 305 306 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) { 307 nsAutoCString nameStr; 308 request->GetName(nameStr); 309 LOG(("LOADGROUP [%p]: Suspending request %p %s.\n", this, request.get(), 310 nameStr.get())); 311 } 312 313 // Suspend the request... 314 rv = request->Suspend(); 315 316 // Remember the first failure and return it... 317 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv; 318 } 319 320 return firstError; 321 } 322 323 NS_IMETHODIMP 324 nsLoadGroup::Resume() { 325 nsresult rv, firstError; 326 uint32_t count = mRequests.EntryCount(); 327 328 AutoTArray<nsIRequest*, 8> requests; 329 330 if (!AppendRequestsToArray(&mRequests, &requests)) { 331 return NS_ERROR_OUT_OF_MEMORY; 332 } 333 334 firstError = NS_OK; 335 // 336 // Operate the elements from back to front so that if items get 337 // get removed from the list it won't affect our iteration 338 // 339 while (count > 0) { 340 nsCOMPtr<nsIRequest> request = dont_AddRef(requests.ElementAt(--count)); 341 342 NS_ASSERTION(request, "NULL request found in list."); 343 if (!request) continue; 344 345 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) { 346 nsAutoCString nameStr; 347 request->GetName(nameStr); 348 LOG(("LOADGROUP [%p]: Resuming request %p %s.\n", this, request.get(), 349 nameStr.get())); 350 } 351 352 // Resume the request... 353 rv = request->Resume(); 354 355 // Remember the first failure and return it... 356 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError)) firstError = rv; 357 } 358 359 return firstError; 360 } 361 362 NS_IMETHODIMP 363 nsLoadGroup::GetLoadFlags(uint32_t* aLoadFlags) { 364 *aLoadFlags = mLoadFlags; 365 return NS_OK; 366 } 367 368 NS_IMETHODIMP 369 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags) { 370 mLoadFlags = aLoadFlags; 371 return NS_OK; 372 } 373 374 NS_IMETHODIMP 375 nsLoadGroup::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { 376 return GetTRRModeImpl(aTRRMode); 377 } 378 379 NS_IMETHODIMP 380 nsLoadGroup::SetTRRMode(nsIRequest::TRRMode aTRRMode) { 381 return SetTRRModeImpl(aTRRMode); 382 } 383 384 NS_IMETHODIMP 385 nsLoadGroup::GetLoadGroup(nsILoadGroup** loadGroup) { 386 nsCOMPtr<nsILoadGroup> result = mLoadGroup; 387 result.forget(loadGroup); 388 return NS_OK; 389 } 390 391 NS_IMETHODIMP 392 nsLoadGroup::SetLoadGroup(nsILoadGroup* loadGroup) { 393 mLoadGroup = loadGroup; 394 return NS_OK; 395 } 396 397 //////////////////////////////////////////////////////////////////////////////// 398 // nsILoadGroup methods: 399 400 NS_IMETHODIMP 401 nsLoadGroup::GetDefaultLoadRequest(nsIRequest** aRequest) { 402 nsCOMPtr<nsIRequest> result = mDefaultLoadRequest; 403 result.forget(aRequest); 404 return NS_OK; 405 } 406 407 NS_IMETHODIMP 408 nsLoadGroup::SetDefaultLoadRequest(nsIRequest* aRequest) { 409 LOG(("nsLoadGroup::SetDefaultLoadRequest this=%p default-request=%p", this, 410 aRequest)); 411 412 mDefaultLoadRequest = aRequest; 413 // Inherit the group load flags from the default load request 414 if (mDefaultLoadRequest) { 415 mDefaultLoadRequest->GetLoadFlags(&mLoadFlags); 416 // 417 // Mask off any bits that are not part of the nsIRequest flags. 418 // in particular, nsIChannel::LOAD_DOCUMENT_URI... 419 // 420 mLoadFlags &= nsIRequest::LOAD_REQUESTMASK; 421 422 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(aRequest); 423 mDefaultLoadIsTimed = timedChannel != nullptr; 424 if (mDefaultLoadIsTimed) { 425 timedChannel->GetChannelCreation(&mDefaultRequestCreationTime); 426 } 427 } 428 // Else, do not change the group's load flags (see bug 95981) 429 return NS_OK; 430 } 431 432 NS_IMETHODIMP 433 nsLoadGroup::AddRequest(nsIRequest* request, nsISupports* ctxt) { 434 nsresult rv; 435 436 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) { 437 nsAutoCString nameStr; 438 request->GetName(nameStr); 439 LOG(("LOADGROUP [%p]: Adding request %p %s (count=%d).\n", this, request, 440 nameStr.get(), mRequests.EntryCount())); 441 } 442 443 NS_ASSERTION(!mRequests.Search(request), 444 "Entry added to loadgroup twice, don't do that"); 445 446 // 447 // Do not add the channel, if the loadgroup is being canceled... 448 // 449 if (mIsCanceling) { 450 LOG( 451 ("LOADGROUP [%p]: AddChannel() ABORTED because LoadGroup is" 452 " being canceled!!\n", 453 this)); 454 455 return NS_BINDING_ABORTED; 456 } 457 458 nsLoadFlags flags; 459 // if the request is the default load request or if the default load 460 // request is null, then the load group should inherit its load flags from 461 // the request, but also we need to enforce defaultLoadFlags. 462 if (mDefaultLoadRequest == request || !mDefaultLoadRequest) { 463 rv = MergeDefaultLoadFlags(request, flags); 464 } else { 465 rv = MergeLoadFlags(request, flags); 466 } 467 if (NS_FAILED(rv)) return rv; 468 469 // 470 // Add the request to the list of active requests... 471 // 472 473 auto* entry = static_cast<RequestMapEntry*>(mRequests.Add(request, fallible)); 474 if (!entry) { 475 return NS_ERROR_OUT_OF_MEMORY; 476 } 477 478 if (mPriority != 0) RescheduleRequest(request, mPriority); 479 480 bool foreground = !(flags & nsIRequest::LOAD_BACKGROUND); 481 if (foreground) { 482 // Update the count of foreground URIs.. 483 mForegroundCount += 1; 484 } 485 486 if (foreground || mNotifyObserverAboutBackgroundRequests) { 487 // 488 // Fire the OnStartRequest notification out to the observer... 489 // 490 // If the notification fails then DO NOT add the request to 491 // the load group. 492 // 493 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver); 494 if (observer) { 495 LOG( 496 ("LOADGROUP [%p]: Firing OnStartRequest for request %p." 497 "(foreground count=%d).\n", 498 this, request, mForegroundCount)); 499 500 rv = observer->OnStartRequest(request); 501 if (NS_FAILED(rv)) { 502 LOG(("LOADGROUP [%p]: OnStartRequest for request %p FAILED.\n", this, 503 request)); 504 // 505 // The URI load has been canceled by the observer. Clean up 506 // the damage... 507 // 508 509 mRequests.Remove(request); 510 511 rv = NS_OK; 512 513 if (foreground) { 514 mForegroundCount -= 1; 515 } 516 } 517 } 518 519 // Ensure that we're part of our loadgroup while pending 520 if (foreground && mForegroundCount == 1 && mLoadGroup) { 521 mLoadGroup->AddRequest(this, nullptr); 522 } 523 } 524 525 return rv; 526 } 527 528 NS_IMETHODIMP 529 nsLoadGroup::RemoveRequest(nsIRequest* request, nsISupports* ctxt, 530 nsresult aStatus) { 531 // Make sure we have a owning reference to the request we're about 532 // to remove. 533 nsCOMPtr<nsIRequest> kungFuDeathGrip(request); 534 535 nsresult rv = RemoveRequestFromHashtable(request, aStatus); 536 if (NS_FAILED(rv)) { 537 return rv; 538 } 539 540 return NotifyRemovalObservers(request, aStatus); 541 } 542 543 static uint64_t GetTransferSize(nsITimedChannel* aTimedChannel) { 544 if (nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aTimedChannel)) { 545 uint64_t size = 0; 546 (void)channel->GetTransferSize(&size); 547 return size; 548 } 549 550 return 0; 551 } 552 553 nsresult nsLoadGroup::RemoveRequestFromHashtable(nsIRequest* request, 554 nsresult aStatus) { 555 NS_ENSURE_ARG_POINTER(request); 556 nsresult rv; 557 558 if (MOZ_LOG_TEST(gLoadGroupLog, LogLevel::Debug)) { 559 nsAutoCString nameStr; 560 request->GetName(nameStr); 561 LOG(("LOADGROUP [%p]: Removing request %p %s status %" PRIx32 562 " (count=%d).\n", 563 this, request, nameStr.get(), static_cast<uint32_t>(aStatus), 564 mRequests.EntryCount() - 1)); 565 } 566 567 // 568 // Remove the request from the group. If this fails, it means that 569 // the request was *not* in the group so do not update the foreground 570 // count or it will get messed up... 571 // 572 auto* entry = static_cast<RequestMapEntry*>(mRequests.Search(request)); 573 574 if (!entry) { 575 LOG(("LOADGROUP [%p]: Unable to remove request %p. Not in group!\n", this, 576 request)); 577 578 return NS_ERROR_FAILURE; 579 } 580 581 mRequests.RemoveEntry(entry); 582 583 // Cache the status of mDefaultLoadRequest, It'll be used later in 584 // TelemetryReport. 585 if (request == mDefaultLoadRequest) { 586 mDefaultStatus = aStatus; 587 } 588 589 // Collect telemetry stats only when default request is a timed channel. 590 // Don't include failed requests in the timing statistics. 591 if (mDefaultLoadIsTimed && NS_SUCCEEDED(aStatus)) { 592 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(request); 593 if (timedChannel) { 594 // Figure out if this request was served from the cache 595 ++mTimedRequests; 596 TimeStamp timeStamp; 597 rv = timedChannel->GetCacheReadStart(&timeStamp); 598 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) { 599 ++mCachedRequests; 600 } 601 602 if (request == mDefaultLoadRequest) { 603 TelemetryReportChannel(timedChannel, true); 604 mPageSize = GetTransferSize(timedChannel); 605 } else { 606 rv = timedChannel->GetAsyncOpen(&timeStamp); 607 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) { 608 glean::http::subitem_open_latency_time.AccumulateRawDuration( 609 timeStamp - mDefaultRequestCreationTime); 610 } 611 612 rv = timedChannel->GetResponseStart(&timeStamp); 613 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) { 614 glean::http::subitem_first_byte_latency_time.AccumulateRawDuration( 615 timeStamp - mDefaultRequestCreationTime); 616 } 617 618 TelemetryReportChannel(timedChannel, false); 619 mTotalSubresourcesSize += GetTransferSize(timedChannel); 620 } 621 } 622 } 623 624 if (mRequests.EntryCount() == 0) { 625 TelemetryReport(); 626 } 627 628 return NS_OK; 629 } 630 631 nsresult nsLoadGroup::NotifyRemovalObservers(nsIRequest* request, 632 nsresult aStatus) { 633 NS_ENSURE_ARG_POINTER(request); 634 // Undo any group priority delta... 635 if (mPriority != 0) RescheduleRequest(request, -mPriority); 636 637 nsLoadFlags flags; 638 nsresult rv = request->GetLoadFlags(&flags); 639 if (NS_FAILED(rv)) return rv; 640 641 bool foreground = !(flags & nsIRequest::LOAD_BACKGROUND); 642 if (foreground) { 643 NS_ASSERTION(mForegroundCount > 0, "ForegroundCount messed up"); 644 mForegroundCount -= 1; 645 } 646 647 if (foreground || mNotifyObserverAboutBackgroundRequests) { 648 // Fire the OnStopRequest out to the observer... 649 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver); 650 if (observer) { 651 LOG( 652 ("LOADGROUP [%p]: Firing OnStopRequest for request %p." 653 "(foreground count=%d).\n", 654 this, request, mForegroundCount)); 655 656 rv = observer->OnStopRequest(request, aStatus); 657 658 if (NS_FAILED(rv)) { 659 LOG(("LOADGROUP [%p]: OnStopRequest for request %p FAILED.\n", this, 660 request)); 661 } 662 } 663 664 // If that was the last request -> remove ourselves from loadgroup 665 if (foreground && mForegroundCount == 0 && mLoadGroup) { 666 mLoadGroup->RemoveRequest(this, nullptr, aStatus); 667 } 668 } 669 670 return rv; 671 } 672 673 NS_IMETHODIMP 674 nsLoadGroup::GetRequests(nsISimpleEnumerator** aRequests) { 675 nsCOMArray<nsIRequest> requests; 676 requests.SetCapacity(mRequests.EntryCount()); 677 678 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) { 679 auto* e = static_cast<RequestMapEntry*>(iter.Get()); 680 requests.AppendObject(e->mKey); 681 } 682 683 return NS_NewArrayEnumerator(aRequests, requests, NS_GET_IID(nsIRequest)); 684 } 685 686 NS_IMETHODIMP 687 nsLoadGroup::GetTotalKeepAliveBytes(uint64_t* aTotalKeepAliveBytes) { 688 MOZ_ASSERT(aTotalKeepAliveBytes); 689 *aTotalKeepAliveBytes = mPendingKeepaliveRequestSize; 690 return NS_OK; 691 } 692 693 NS_IMETHODIMP 694 nsLoadGroup::SetTotalKeepAliveBytes(uint64_t aTotalKeepAliveBytes) { 695 mPendingKeepaliveRequestSize = aTotalKeepAliveBytes; 696 return NS_OK; 697 } 698 699 NS_IMETHODIMP 700 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver) { 701 SetGroupObserver(aObserver, false); 702 return NS_OK; 703 } 704 705 void nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver, 706 bool aIncludeBackgroundRequests) { 707 mObserver = do_GetWeakReference(aObserver); 708 mNotifyObserverAboutBackgroundRequests = aIncludeBackgroundRequests; 709 } 710 711 NS_IMETHODIMP 712 nsLoadGroup::GetGroupObserver(nsIRequestObserver** aResult) { 713 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver); 714 observer.forget(aResult); 715 return NS_OK; 716 } 717 718 NS_IMETHODIMP 719 nsLoadGroup::GetActiveCount(uint32_t* aResult) { 720 *aResult = mForegroundCount; 721 return NS_OK; 722 } 723 724 NS_IMETHODIMP 725 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { 726 NS_ENSURE_ARG_POINTER(aCallbacks); 727 nsCOMPtr<nsIInterfaceRequestor> callbacks = mCallbacks; 728 callbacks.forget(aCallbacks); 729 return NS_OK; 730 } 731 732 NS_IMETHODIMP 733 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) { 734 mCallbacks = aCallbacks; 735 return NS_OK; 736 } 737 738 NS_IMETHODIMP 739 nsLoadGroup::GetRequestContextID(uint64_t* aRCID) { 740 if (!mRequestContext) { 741 return NS_ERROR_NOT_AVAILABLE; 742 } 743 *aRCID = mRequestContext->GetID(); 744 return NS_OK; 745 } 746 747 //////////////////////////////////////////////////////////////////////////////// 748 // nsILoadGroupChild methods: 749 750 NS_IMETHODIMP 751 nsLoadGroup::GetParentLoadGroup(nsILoadGroup** aParentLoadGroup) { 752 *aParentLoadGroup = nullptr; 753 nsCOMPtr<nsILoadGroup> parent = do_QueryReferent(mParentLoadGroup); 754 if (!parent) return NS_OK; 755 parent.forget(aParentLoadGroup); 756 return NS_OK; 757 } 758 759 NS_IMETHODIMP 760 nsLoadGroup::SetParentLoadGroup(nsILoadGroup* aParentLoadGroup) { 761 mParentLoadGroup = do_GetWeakReference(aParentLoadGroup); 762 return NS_OK; 763 } 764 765 NS_IMETHODIMP 766 nsLoadGroup::GetChildLoadGroup(nsILoadGroup** aChildLoadGroup) { 767 *aChildLoadGroup = do_AddRef(this).take(); 768 return NS_OK; 769 } 770 771 NS_IMETHODIMP 772 nsLoadGroup::GetRootLoadGroup(nsILoadGroup** aRootLoadGroup) { 773 // first recursively try the root load group of our parent 774 nsCOMPtr<nsILoadGroupChild> ancestor = do_QueryReferent(mParentLoadGroup); 775 if (ancestor) return ancestor->GetRootLoadGroup(aRootLoadGroup); 776 777 // next recursively try the root load group of our own load grop 778 ancestor = do_QueryInterface(mLoadGroup); 779 if (ancestor) return ancestor->GetRootLoadGroup(aRootLoadGroup); 780 781 // finally just return this 782 *aRootLoadGroup = do_AddRef(this).take(); 783 return NS_OK; 784 } 785 786 //////////////////////////////////////////////////////////////////////////////// 787 // nsISupportsPriority methods: 788 789 NS_IMETHODIMP 790 nsLoadGroup::GetPriority(int32_t* aValue) { 791 *aValue = mPriority; 792 return NS_OK; 793 } 794 795 NS_IMETHODIMP 796 nsLoadGroup::SetPriority(int32_t aValue) { 797 return AdjustPriority(aValue - mPriority); 798 } 799 800 NS_IMETHODIMP 801 nsLoadGroup::AdjustPriority(int32_t aDelta) { 802 // Update the priority for each request that supports nsISupportsPriority 803 if (aDelta != 0) { 804 mPriority += aDelta; 805 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) { 806 auto* e = static_cast<RequestMapEntry*>(iter.Get()); 807 RescheduleRequest(e->mKey, aDelta); 808 } 809 } 810 return NS_OK; 811 } 812 813 NS_IMETHODIMP 814 nsLoadGroup::GetDefaultLoadFlags(uint32_t* aFlags) { 815 *aFlags = mDefaultLoadFlags; 816 return NS_OK; 817 } 818 819 NS_IMETHODIMP 820 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags) { 821 mDefaultLoadFlags = aFlags; 822 return NS_OK; 823 } 824 825 //////////////////////////////////////////////////////////////////////////////// 826 827 void nsLoadGroup::TelemetryReport() { 828 // We should only report HTTP_PAGE_* telemetry if the defaultRequest was 829 // actually successful. 830 if (mDefaultLoadIsTimed && NS_SUCCEEDED(mDefaultStatus)) { 831 glean::http::request_per_page.AccumulateSingleSample(mTimedRequests); 832 if (mTimedRequests) { 833 glean::http::request_per_page_from_cache.AccumulateSingleSample( 834 mCachedRequests * 100 / mTimedRequests); 835 } 836 } 837 838 mTimedRequests = 0; 839 mCachedRequests = 0; 840 mDefaultLoadIsTimed = false; 841 } 842 843 void nsLoadGroup::TelemetryReportChannel(nsITimedChannel* aTimedChannel, 844 bool aDefaultRequest) { 845 nsresult rv; 846 847 TimeStamp asyncOpen; 848 rv = aTimedChannel->GetAsyncOpen(&asyncOpen); 849 // We do not check !asyncOpen.IsNull() bellow, prevent ASSERTIONs this way 850 if (NS_FAILED(rv) || asyncOpen.IsNull()) return; 851 852 TimeStamp cacheReadStart; 853 rv = aTimedChannel->GetCacheReadStart(&cacheReadStart); 854 if (NS_FAILED(rv)) return; 855 856 TimeStamp cacheReadEnd; 857 rv = aTimedChannel->GetCacheReadEnd(&cacheReadEnd); 858 if (NS_FAILED(rv)) return; 859 860 TimeStamp domainLookupStart; 861 rv = aTimedChannel->GetDomainLookupStart(&domainLookupStart); 862 if (NS_FAILED(rv)) return; 863 864 TimeStamp domainLookupEnd; 865 rv = aTimedChannel->GetDomainLookupEnd(&domainLookupEnd); 866 if (NS_FAILED(rv)) return; 867 868 TimeStamp connectStart; 869 rv = aTimedChannel->GetConnectStart(&connectStart); 870 if (NS_FAILED(rv)) return; 871 872 TimeStamp secureConnectionStart; 873 rv = aTimedChannel->GetSecureConnectionStart(&secureConnectionStart); 874 if (NS_FAILED(rv)) return; 875 876 TimeStamp connectEnd; 877 rv = aTimedChannel->GetConnectEnd(&connectEnd); 878 if (NS_FAILED(rv)) return; 879 880 TimeStamp requestStart; 881 rv = aTimedChannel->GetRequestStart(&requestStart); 882 if (NS_FAILED(rv)) return; 883 884 TimeStamp responseStart; 885 rv = aTimedChannel->GetResponseStart(&responseStart); 886 if (NS_FAILED(rv)) return; 887 888 TimeStamp responseEnd; 889 rv = aTimedChannel->GetResponseEnd(&responseEnd); 890 if (NS_FAILED(rv)) return; 891 #ifndef ANDROID 892 bool useHttp3 = false; 893 #endif 894 bool supportHttp3 = false; 895 nsCOMPtr<nsIHttpChannelInternal> httpChannel = 896 do_QueryInterface(aTimedChannel); 897 if (httpChannel) { 898 uint32_t major; 899 uint32_t minor; 900 if (NS_SUCCEEDED(httpChannel->GetResponseVersion(&major, &minor))) { 901 #ifndef ANDROID 902 useHttp3 = major == 3; 903 #endif 904 if (major == 2) { 905 if (NS_FAILED(httpChannel->GetSupportsHTTP3(&supportHttp3))) { 906 supportHttp3 = false; 907 } 908 } 909 } 910 } 911 912 // Glean instrumentation of metrics previously collected via Geckoview 913 // Streaming. 914 if (!domainLookupStart.IsNull()) { 915 if (aDefaultRequest) { 916 mozilla::glean::network::dns_start.AccumulateRawDuration( 917 domainLookupStart - asyncOpen); 918 if (!domainLookupEnd.IsNull()) { 919 mozilla::glean::network::dns_end.AccumulateRawDuration( 920 domainLookupEnd - domainLookupStart); 921 } 922 } 923 #ifndef ANDROID 924 else { 925 mozilla::glean::network::sub_dns_start.AccumulateRawDuration( 926 domainLookupStart - asyncOpen); 927 if (!domainLookupEnd.IsNull()) { 928 mozilla::glean::network::sub_dns_end.AccumulateRawDuration( 929 domainLookupEnd - domainLookupStart); 930 } 931 } 932 #endif 933 } 934 if (!connectEnd.IsNull()) { 935 if (!connectStart.IsNull()) { 936 if (aDefaultRequest) { 937 mozilla::glean::network::tcp_connection.AccumulateRawDuration( 938 connectEnd - connectStart); 939 } 940 #ifndef ANDROID 941 else { 942 mozilla::glean::network::sub_tcp_connection.AccumulateRawDuration( 943 connectEnd - connectStart); 944 } 945 #endif 946 } 947 if (!secureConnectionStart.IsNull()) { 948 if (aDefaultRequest) { 949 mozilla::glean::network::tls_handshake.AccumulateRawDuration( 950 connectEnd - secureConnectionStart); 951 } 952 #ifndef ANDROID 953 else { 954 mozilla::glean::network::sub_tls_handshake.AccumulateRawDuration( 955 connectEnd - secureConnectionStart); 956 } 957 #endif 958 } 959 } 960 if (!requestStart.IsNull() && !responseEnd.IsNull()) { 961 if (aDefaultRequest) { 962 mozilla::glean::network::open_to_first_sent.AccumulateRawDuration( 963 requestStart - asyncOpen); 964 mozilla::glean::network::first_sent_to_last_received 965 .AccumulateRawDuration(responseEnd - requestStart); 966 967 if (cacheReadStart.IsNull() && !responseStart.IsNull()) { 968 mozilla::glean::network::open_to_first_received.AccumulateRawDuration( 969 responseStart - asyncOpen); 970 } 971 } 972 #ifndef ANDROID 973 else { 974 mozilla::glean::network::sub_open_to_first_sent.AccumulateRawDuration( 975 requestStart - asyncOpen); 976 mozilla::glean::network::sub_first_sent_to_last_received 977 .AccumulateRawDuration(responseEnd - requestStart); 978 if (cacheReadStart.IsNull() && !responseStart.IsNull()) { 979 mozilla::glean::network::sub_open_to_first_received 980 .AccumulateRawDuration(responseStart - asyncOpen); 981 } 982 } 983 #endif 984 } 985 if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) { 986 if (aDefaultRequest) { 987 mozilla::glean::network::first_from_cache.AccumulateRawDuration( 988 cacheReadStart - asyncOpen); 989 #ifndef ANDROID 990 mozilla::glean::network::cache_read_time.AccumulateRawDuration( 991 cacheReadEnd - cacheReadStart); 992 if (!requestStart.IsNull() && !responseEnd.IsNull()) { 993 mozilla::glean::network::http_revalidation.AccumulateRawDuration( 994 responseEnd - requestStart); 995 } 996 #endif 997 } 998 #ifndef ANDROID 999 else { 1000 mozilla::glean::network::sub_first_from_cache.AccumulateRawDuration( 1001 cacheReadStart - asyncOpen); 1002 mozilla::glean::network::sub_cache_read_time.AccumulateRawDuration( 1003 cacheReadEnd - cacheReadStart); 1004 if (!requestStart.IsNull() && !responseEnd.IsNull()) { 1005 mozilla::glean::network::sub_http_revalidation.AccumulateRawDuration( 1006 responseEnd - requestStart); 1007 } 1008 } 1009 #endif 1010 } 1011 #ifndef ANDROID 1012 if (!cacheReadEnd.IsNull()) { 1013 if (aDefaultRequest) { 1014 mozilla::glean::network::complete_load.AccumulateRawDuration( 1015 cacheReadEnd - asyncOpen); 1016 mozilla::glean::network::complete_load_cached.AccumulateRawDuration( 1017 cacheReadEnd - asyncOpen); 1018 } else { 1019 mozilla::glean::network::sub_complete_load.AccumulateRawDuration( 1020 cacheReadEnd - asyncOpen); 1021 mozilla::glean::network::sub_complete_load_cached.AccumulateRawDuration( 1022 cacheReadEnd - asyncOpen); 1023 } 1024 } else if (!responseEnd.IsNull()) { 1025 if (aDefaultRequest) { 1026 mozilla::glean::network::complete_load.AccumulateRawDuration(responseEnd - 1027 asyncOpen); 1028 mozilla::glean::network::complete_load_net.AccumulateRawDuration( 1029 responseEnd - asyncOpen); 1030 } else { 1031 mozilla::glean::network::sub_complete_load.AccumulateRawDuration( 1032 responseEnd - asyncOpen); 1033 mozilla::glean::network::sub_complete_load_net.AccumulateRawDuration( 1034 responseEnd - asyncOpen); 1035 } 1036 } 1037 #endif 1038 1039 #ifndef ANDROID 1040 if ((useHttp3 || supportHttp3) && cacheReadStart.IsNull() && 1041 cacheReadEnd.IsNull()) { 1042 nsCString key = (useHttp3) ? ((aDefaultRequest) ? "uses_http3_page"_ns 1043 : "uses_http3_sub"_ns) 1044 : ((aDefaultRequest) ? "supports_http3_page"_ns 1045 : "supports_http3_sub"_ns); 1046 1047 if (!secureConnectionStart.IsNull() && !connectEnd.IsNull()) { 1048 mozilla::glean::network::http3_tls_handshake.Get(key) 1049 .AccumulateRawDuration(connectEnd - secureConnectionStart); 1050 } 1051 1052 if (supportHttp3 && !connectStart.IsNull() && !connectEnd.IsNull()) { 1053 mozilla::glean::network::sup_http3_tcp_connection.Get(key) 1054 .AccumulateRawDuration(connectEnd - connectStart); 1055 } 1056 1057 if (!requestStart.IsNull() && !responseEnd.IsNull()) { 1058 mozilla::glean::network::http3_open_to_first_sent.Get(key) 1059 .AccumulateRawDuration(requestStart - asyncOpen); 1060 1061 mozilla::glean::network::http3_first_sent_to_last_received.Get(key) 1062 .AccumulateRawDuration(responseEnd - requestStart); 1063 1064 if (!responseStart.IsNull()) { 1065 mozilla::glean::network::http3_open_to_first_received.Get(key) 1066 .AccumulateRawDuration(responseStart - asyncOpen); 1067 } 1068 1069 if (!responseEnd.IsNull()) { 1070 mozilla::glean::network::http3_complete_load.Get(key) 1071 .AccumulateRawDuration(responseEnd - asyncOpen); 1072 } 1073 } 1074 } 1075 #endif 1076 1077 bool hasHTTPSRR = false; 1078 if (httpChannel && NS_SUCCEEDED(httpChannel->GetHasHTTPSRR(&hasHTTPSRR)) && 1079 cacheReadStart.IsNull() && cacheReadEnd.IsNull() && 1080 !requestStart.IsNull()) { 1081 TimeDuration elapsed = requestStart - asyncOpen; 1082 if (hasHTTPSRR) { 1083 if (aDefaultRequest) { 1084 glean::networking::http_channel_page_open_to_first_sent_https_rr 1085 .AccumulateRawDuration(elapsed); 1086 } else { 1087 glean::networking::http_channel_sub_open_to_first_sent_https_rr 1088 .AccumulateRawDuration(elapsed); 1089 } 1090 } else { 1091 if (aDefaultRequest) { 1092 glean::networking::http_channel_page_open_to_first_sent 1093 .AccumulateRawDuration(elapsed); 1094 } else { 1095 glean::networking::http_channel_sub_open_to_first_sent 1096 .AccumulateRawDuration(elapsed); 1097 } 1098 } 1099 } 1100 } 1101 1102 nsresult nsLoadGroup::MergeLoadFlags(nsIRequest* aRequest, 1103 nsLoadFlags& outFlags) { 1104 nsresult rv; 1105 nsLoadFlags flags, oldFlags; 1106 1107 rv = aRequest->GetLoadFlags(&flags); 1108 if (NS_FAILED(rv)) { 1109 return rv; 1110 } 1111 1112 oldFlags = flags; 1113 1114 // Inherit some bits... 1115 flags |= mLoadFlags & kInheritedLoadFlags; 1116 1117 // ... and force the default flags. 1118 flags |= mDefaultLoadFlags; 1119 1120 if (flags != oldFlags) { 1121 rv = aRequest->SetLoadFlags(flags); 1122 } 1123 1124 outFlags = flags; 1125 return rv; 1126 } 1127 1128 nsresult nsLoadGroup::MergeDefaultLoadFlags(nsIRequest* aRequest, 1129 nsLoadFlags& outFlags) { 1130 nsresult rv; 1131 nsLoadFlags flags, oldFlags; 1132 1133 rv = aRequest->GetLoadFlags(&flags); 1134 if (NS_FAILED(rv)) { 1135 return rv; 1136 } 1137 1138 oldFlags = flags; 1139 // ... and force the default flags. 1140 flags |= mDefaultLoadFlags; 1141 1142 if (flags != oldFlags) { 1143 rv = aRequest->SetLoadFlags(flags); 1144 } 1145 outFlags = flags; 1146 return rv; 1147 } 1148 1149 nsresult nsLoadGroup::Init() { 1150 mRequestContextService = RequestContextService::GetOrCreate(); 1151 if (mRequestContextService) { 1152 (void)mRequestContextService->NewRequestContext( 1153 getter_AddRefs(mRequestContext)); 1154 } 1155 1156 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1157 NS_ENSURE_STATE(os); 1158 1159 (void)os->AddObserver(this, "last-pb-context-exited", true); 1160 1161 return NS_OK; 1162 } 1163 1164 nsresult nsLoadGroup::InitWithRequestContextId( 1165 const uint64_t& aRequestContextId) { 1166 mRequestContextService = RequestContextService::GetOrCreate(); 1167 if (mRequestContextService) { 1168 (void)mRequestContextService->GetRequestContext( 1169 aRequestContextId, getter_AddRefs(mRequestContext)); 1170 } 1171 mExternalRequestContext = true; 1172 1173 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1174 NS_ENSURE_STATE(os); 1175 1176 (void)os->AddObserver(this, "last-pb-context-exited", true); 1177 1178 return NS_OK; 1179 } 1180 1181 NS_IMETHODIMP 1182 nsLoadGroup::Observe(nsISupports* aSubject, const char* aTopic, 1183 const char16_t* aData) { 1184 MOZ_ASSERT(!strcmp(aTopic, "last-pb-context-exited")); 1185 1186 OriginAttributes attrs; 1187 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs); 1188 if (!attrs.IsPrivateBrowsing()) { 1189 return NS_OK; 1190 } 1191 1192 mBrowsingContextDiscarded = true; 1193 return NS_OK; 1194 } 1195 1196 NS_IMETHODIMP 1197 nsLoadGroup::GetIsBrowsingContextDiscarded(bool* aIsBrowsingContextDiscarded) { 1198 *aIsBrowsingContextDiscarded = mBrowsingContextDiscarded; 1199 return NS_OK; 1200 } 1201 1202 } // namespace net 1203 } // namespace mozilla 1204 1205 #undef LOG