HTMLDNSPrefetch.cpp (21654B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 "HTMLDNSPrefetch.h" 8 9 #include "base/basictypes.h" 10 #include "mozilla/Components.h" 11 #include "mozilla/OriginAttributes.h" 12 #include "mozilla/Preferences.h" 13 #include "mozilla/StaticPrefs_network.h" 14 #include "mozilla/StoragePrincipalHelper.h" 15 #include "mozilla/dom/Document.h" 16 #include "mozilla/dom/Element.h" 17 #include "mozilla/dom/HTMLAnchorElement.h" 18 #include "mozilla/dom/HTMLLinkElement.h" 19 #include "mozilla/net/NeckoChild.h" 20 #include "mozilla/net/NeckoCommon.h" 21 #include "nsCOMPtr.h" 22 #include "nsGkAtoms.h" 23 #include "nsICancelable.h" 24 #include "nsIDNSListener.h" 25 #include "nsIDNSRecord.h" 26 #include "nsIDNSService.h" 27 #include "nsIObserverService.h" 28 #include "nsIProtocolHandler.h" 29 #include "nsITimer.h" 30 #include "nsIWebProgress.h" 31 #include "nsIWebProgressListener.h" 32 #include "nsNetCID.h" 33 #include "nsNetUtil.h" 34 #include "nsString.h" 35 #include "nsThreadUtils.h" 36 #include "nsURLHelper.h" 37 38 using namespace mozilla::net; 39 40 namespace mozilla::dom { 41 42 class NoOpDNSListener final : public nsIDNSListener { 43 // This class exists to give a safe callback no-op DNSListener 44 public: 45 NS_DECL_THREADSAFE_ISUPPORTS 46 NS_DECL_NSIDNSLISTENER 47 48 NoOpDNSListener() = default; 49 50 private: 51 ~NoOpDNSListener() = default; 52 }; 53 54 NS_IMPL_ISUPPORTS(NoOpDNSListener, nsIDNSListener) 55 56 NS_IMETHODIMP 57 NoOpDNSListener::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec, 58 nsresult status) { 59 return NS_OK; 60 } 61 62 // This is just a (size) optimization and could be avoided by storing the 63 // SupportsDNSPrefetch pointer of the element in the prefetch queue, but given 64 // we need this for GetURIForDNSPrefetch... 65 static SupportsDNSPrefetch& ToSupportsDNSPrefetch(Element& aElement) { 66 if (auto* link = HTMLLinkElement::FromNode(aElement)) { 67 return *link; 68 } 69 auto* anchor = HTMLAnchorElement::FromNode(aElement); 70 MOZ_DIAGNOSTIC_ASSERT(anchor); 71 return *anchor; 72 } 73 74 nsIURI* SupportsDNSPrefetch::GetURIForDNSPrefetch(Element& aElement) { 75 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == this); 76 if (auto* link = HTMLLinkElement::FromNode(aElement)) { 77 return link->GetURI(); 78 } 79 auto* anchor = HTMLAnchorElement::FromNode(aElement); 80 MOZ_DIAGNOSTIC_ASSERT(anchor); 81 return anchor->GetURI(); 82 } 83 84 class DeferredDNSPrefetches final : public nsIWebProgressListener, 85 public nsSupportsWeakReference, 86 public nsIObserver { 87 public: 88 NS_DECL_ISUPPORTS 89 NS_DECL_NSIWEBPROGRESSLISTENER 90 NS_DECL_NSIOBSERVER 91 92 DeferredDNSPrefetches(); 93 94 void Activate(); 95 nsresult Add(nsIDNSService::DNSFlags flags, SupportsDNSPrefetch&, Element&); 96 97 void RemoveUnboundLinks(); 98 99 private: 100 ~DeferredDNSPrefetches(); 101 void Flush(); 102 103 void SubmitQueue(); 104 void SubmitQueueEntry(Element& aElement, nsIDNSService::DNSFlags aFlags); 105 106 uint16_t mHead; 107 uint16_t mTail; 108 uint32_t mActiveLoaderCount; 109 110 nsCOMPtr<nsITimer> mTimer; 111 bool mTimerArmed; 112 static void Tick(nsITimer* aTimer, void* aClosure); 113 114 static const int sMaxDeferred = 512; // keep power of 2 for masking 115 static const int sMaxDeferredMask = (sMaxDeferred - 1); 116 117 struct deferred_entry { 118 nsIDNSService::DNSFlags mFlags; 119 // SupportsDNSPrefetch clears this raw pointer in Destroyed(). 120 Element* mElement; 121 } mEntries[sMaxDeferred]; 122 }; 123 124 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID); 125 static bool sInitialized = false; 126 static nsIDNSService* sDNSService = nullptr; 127 static DeferredDNSPrefetches* sPrefetches = nullptr; 128 static NoOpDNSListener* sDNSListener = nullptr; 129 130 nsresult HTMLDNSPrefetch::Initialize() { 131 if (sInitialized) { 132 NS_WARNING("Initialize() called twice"); 133 return NS_OK; 134 } 135 136 sPrefetches = new DeferredDNSPrefetches(); 137 NS_ADDREF(sPrefetches); 138 139 sDNSListener = new NoOpDNSListener(); 140 NS_ADDREF(sDNSListener); 141 142 sPrefetches->Activate(); 143 144 if (IsNeckoChild()) NeckoChild::InitNeckoChild(); 145 146 sInitialized = true; 147 return NS_OK; 148 } 149 150 nsresult HTMLDNSPrefetch::Shutdown() { 151 if (!sInitialized) { 152 NS_WARNING("Not Initialized"); 153 return NS_OK; 154 } 155 sInitialized = false; 156 NS_IF_RELEASE(sDNSService); 157 NS_IF_RELEASE(sPrefetches); 158 NS_IF_RELEASE(sDNSListener); 159 160 return NS_OK; 161 } 162 163 static bool EnsureDNSService() { 164 if (sDNSService) { 165 return true; 166 } 167 168 NS_IF_RELEASE(sDNSService); 169 nsresult rv; 170 rv = CallGetService(kDNSServiceCID, &sDNSService); 171 if (NS_FAILED(rv)) { 172 return false; 173 } 174 175 return !!sDNSService; 176 } 177 178 bool HTMLDNSPrefetch::IsAllowed(Document* aDocument) { 179 // Do not use prefetch if the document's node principal is the system 180 // principal. 181 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal(); 182 if (principal->IsSystemPrincipal()) { 183 return false; 184 } 185 186 // There is no need to do prefetch on non UI scenarios such as XMLHttpRequest. 187 return aDocument->IsDNSPrefetchAllowed() && aDocument->GetWindow(); 188 } 189 190 static nsIDNSService::DNSFlags GetDNSFlagsFromElement(Element& aElement) { 191 nsIChannel* channel = aElement.OwnerDoc()->GetChannel(); 192 if (!channel) { 193 return nsIDNSService::RESOLVE_DEFAULT_FLAGS; 194 } 195 return nsIDNSService::GetFlagsFromTRRMode(channel->GetTRRMode()); 196 } 197 198 nsIDNSService::DNSFlags HTMLDNSPrefetch::PriorityToDNSServiceFlags( 199 Priority aPriority) { 200 switch (aPriority) { 201 case Priority::Low: 202 return nsIDNSService::RESOLVE_PRIORITY_LOW; 203 case Priority::Medium: 204 return nsIDNSService::RESOLVE_PRIORITY_MEDIUM; 205 case Priority::High: 206 return nsIDNSService::RESOLVE_DEFAULT_FLAGS; 207 } 208 MOZ_ASSERT_UNREACHABLE("Unknown priority"); 209 return nsIDNSService::RESOLVE_DEFAULT_FLAGS; 210 } 211 212 nsresult HTMLDNSPrefetch::DeferPrefetch(SupportsDNSPrefetch& aSupports, 213 Element& aElement, Priority aPriority) { 214 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == &aSupports); 215 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService()) { 216 return NS_ERROR_NOT_AVAILABLE; 217 } 218 return sPrefetches->Add( 219 GetDNSFlagsFromElement(aElement) | PriorityToDNSServiceFlags(aPriority), 220 aSupports, aElement); 221 } 222 223 nsresult HTMLDNSPrefetch::Prefetch( 224 const nsAString& hostname, bool isHttps, 225 const OriginAttributes& aPartitionedPrincipalOriginAttributes, 226 nsIDNSService::DNSFlags flags) { 227 if (IsNeckoChild()) { 228 // We need to check IsEmpty() because net_IsValidDNSHost() 229 // considers empty strings to be valid hostnames 230 if (!hostname.IsEmpty() && 231 net_IsValidDNSHost(NS_ConvertUTF16toUTF8(hostname))) { 232 // during shutdown gNeckoChild might be null 233 if (gNeckoChild) { 234 gNeckoChild->SendHTMLDNSPrefetch( 235 hostname, isHttps, aPartitionedPrincipalOriginAttributes, flags); 236 } 237 } 238 return NS_OK; 239 } 240 241 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService()) 242 return NS_ERROR_NOT_AVAILABLE; 243 244 nsCOMPtr<nsICancelable> tmpOutstanding; 245 nsresult rv = sDNSService->AsyncResolveNative( 246 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_DEFAULT, 247 flags | nsIDNSService::RESOLVE_SPECULATE, nullptr, sDNSListener, nullptr, 248 aPartitionedPrincipalOriginAttributes, getter_AddRefs(tmpOutstanding)); 249 if (NS_FAILED(rv)) { 250 return rv; 251 } 252 253 if (StaticPrefs::network_dns_upgrade_with_https_rr() || 254 StaticPrefs::network_dns_use_https_rr_as_altsvc()) { 255 (void)sDNSService->AsyncResolveNative( 256 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_HTTPSSVC, 257 flags | nsIDNSService::RESOLVE_SPECULATE, nullptr, sDNSListener, 258 nullptr, aPartitionedPrincipalOriginAttributes, 259 getter_AddRefs(tmpOutstanding)); 260 } 261 262 return NS_OK; 263 } 264 265 nsresult HTMLDNSPrefetch::Prefetch( 266 const nsAString& hostname, bool isHttps, 267 const OriginAttributes& aPartitionedPrincipalOriginAttributes, 268 nsIRequest::TRRMode aMode, Priority aPriority) { 269 return Prefetch(hostname, isHttps, aPartitionedPrincipalOriginAttributes, 270 nsIDNSService::GetFlagsFromTRRMode(aMode) | 271 PriorityToDNSServiceFlags(aPriority)); 272 } 273 274 nsresult HTMLDNSPrefetch::CancelPrefetch(SupportsDNSPrefetch& aSupports, 275 Element& aElement, Priority aPriority, 276 nsresult aReason) { 277 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == &aSupports); 278 279 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService()) { 280 return NS_ERROR_NOT_AVAILABLE; 281 } 282 283 nsIDNSService::DNSFlags flags = 284 GetDNSFlagsFromElement(aElement) | PriorityToDNSServiceFlags(aPriority); 285 286 nsIURI* uri = aSupports.GetURIForDNSPrefetch(aElement); 287 if (!uri) { 288 return NS_OK; 289 } 290 291 nsAutoCString hostname; 292 uri->GetAsciiHost(hostname); 293 294 nsAutoString protocol; 295 bool isHttps = uri->SchemeIs("https"); 296 297 OriginAttributes oa; 298 StoragePrincipalHelper::GetOriginAttributesForNetworkState( 299 aElement.OwnerDoc(), oa); 300 301 return CancelPrefetch(NS_ConvertUTF8toUTF16(hostname), isHttps, oa, flags, 302 aReason); 303 } 304 305 nsresult HTMLDNSPrefetch::CancelPrefetch( 306 const nsAString& hostname, bool isHttps, 307 const OriginAttributes& aPartitionedPrincipalOriginAttributes, 308 nsIDNSService::DNSFlags flags, nsresult aReason) { 309 // Forward this request to Necko Parent if we're a child process 310 if (IsNeckoChild()) { 311 // We need to check IsEmpty() because net_IsValidDNSHost() 312 // considers empty strings to be valid hostnames 313 if (!hostname.IsEmpty() && 314 net_IsValidDNSHost(NS_ConvertUTF16toUTF8(hostname))) { 315 // during shutdown gNeckoChild might be null 316 if (gNeckoChild && gNeckoChild->CanSend()) { 317 gNeckoChild->SendCancelHTMLDNSPrefetch( 318 hostname, isHttps, aPartitionedPrincipalOriginAttributes, flags, 319 aReason); 320 } 321 } 322 return NS_OK; 323 } 324 325 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService()) { 326 return NS_ERROR_NOT_AVAILABLE; 327 } 328 329 // Forward cancellation to DNS service 330 nsresult rv = sDNSService->CancelAsyncResolveNative( 331 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_DEFAULT, 332 flags | nsIDNSService::RESOLVE_SPECULATE, 333 nullptr, // AdditionalInfo 334 sDNSListener, aReason, aPartitionedPrincipalOriginAttributes); 335 336 if (StaticPrefs::network_dns_upgrade_with_https_rr() || 337 StaticPrefs::network_dns_use_https_rr_as_altsvc()) { 338 (void)sDNSService->CancelAsyncResolveNative( 339 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_HTTPSSVC, 340 flags | nsIDNSService::RESOLVE_SPECULATE, 341 nullptr, // AdditionalInfo 342 sDNSListener, aReason, aPartitionedPrincipalOriginAttributes); 343 } 344 return rv; 345 } 346 347 nsresult HTMLDNSPrefetch::CancelPrefetch( 348 const nsAString& hostname, bool isHttps, 349 const OriginAttributes& aPartitionedPrincipalOriginAttributes, 350 nsIRequest::TRRMode aTRRMode, Priority aPriority, nsresult aReason) { 351 return CancelPrefetch(hostname, isHttps, 352 aPartitionedPrincipalOriginAttributes, 353 nsIDNSService::GetFlagsFromTRRMode(aTRRMode) | 354 PriorityToDNSServiceFlags(aPriority), 355 aReason); 356 } 357 358 void HTMLDNSPrefetch::ElementDestroyed(Element& aElement, 359 SupportsDNSPrefetch& aSupports) { 360 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == &aSupports); 361 MOZ_ASSERT(aSupports.IsInDNSPrefetch()); 362 if (sPrefetches) { 363 // Clean up all the possible links at once. 364 sPrefetches->RemoveUnboundLinks(); 365 } 366 } 367 368 void HTMLDNSPrefetch::SendRequest(Element& aElement, 369 nsIDNSService::DNSFlags aFlags) { 370 auto& supports = ToSupportsDNSPrefetch(aElement); 371 372 nsIURI* uri = supports.GetURIForDNSPrefetch(aElement); 373 if (!uri) { 374 return; 375 } 376 377 nsAutoCString hostName; 378 uri->GetAsciiHost(hostName); 379 if (hostName.IsEmpty()) { 380 return; 381 } 382 383 bool isLocalResource = false; 384 nsresult rv = NS_URIChainHasFlags( 385 uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &isLocalResource); 386 if (NS_FAILED(rv) || isLocalResource) { 387 return; 388 } 389 390 OriginAttributes oa; 391 StoragePrincipalHelper::GetOriginAttributesForNetworkState( 392 aElement.OwnerDoc(), oa); 393 394 bool isHttps = uri->SchemeIs("https"); 395 396 if (IsNeckoChild()) { 397 // during shutdown gNeckoChild might be null 398 if (gNeckoChild) { 399 gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), isHttps, 400 oa, aFlags); 401 } 402 } else { 403 nsCOMPtr<nsICancelable> tmpOutstanding; 404 405 rv = sDNSService->AsyncResolveNative( 406 hostName, nsIDNSService::RESOLVE_TYPE_DEFAULT, 407 aFlags | nsIDNSService::RESOLVE_SPECULATE, nullptr, sDNSListener, 408 nullptr, oa, getter_AddRefs(tmpOutstanding)); 409 if (NS_FAILED(rv)) { 410 return; 411 } 412 413 // Fetch HTTPS RR if needed. 414 if (StaticPrefs::network_dns_upgrade_with_https_rr() || 415 StaticPrefs::network_dns_use_https_rr_as_altsvc()) { 416 sDNSService->AsyncResolveNative( 417 hostName, nsIDNSService::RESOLVE_TYPE_HTTPSSVC, 418 aFlags | nsIDNSService::RESOLVE_SPECULATE, nullptr, sDNSListener, 419 nullptr, oa, getter_AddRefs(tmpOutstanding)); 420 } 421 } 422 423 // Tell element that deferred prefetch was requested. 424 supports.DNSPrefetchRequestStarted(); 425 } 426 427 void SupportsDNSPrefetch::TryDNSPrefetch( 428 Element& aOwner, HTMLDNSPrefetch::PrefetchSource aSource) { 429 MOZ_ASSERT(aOwner.IsInComposedDoc()); 430 if (HTMLDNSPrefetch::IsAllowed(aOwner.OwnerDoc())) { 431 if (!(sInitialized && sDNSListener) || !EnsureDNSService()) { 432 return; 433 } 434 435 if (aSource == HTMLDNSPrefetch::PrefetchSource::AnchorSpeculativePrefetch) { 436 HTMLDNSPrefetch::DeferPrefetch(*this, aOwner, 437 HTMLDNSPrefetch::Priority::Low); 438 } else if (aSource == HTMLDNSPrefetch::PrefetchSource::LinkDnsPrefetch) { 439 HTMLDNSPrefetch::SendRequest( 440 aOwner, GetDNSFlagsFromElement(aOwner) | 441 HTMLDNSPrefetch::PriorityToDNSServiceFlags( 442 HTMLDNSPrefetch::Priority::High)); 443 } else { 444 MOZ_ASSERT_UNREACHABLE("Unknown DNS prefetch type"); 445 } 446 } 447 } 448 449 void SupportsDNSPrefetch::CancelDNSPrefetch(Element& aOwner) { 450 // If prefetch was deferred, clear flag and move on 451 if (mDNSPrefetchDeferred) { 452 mDNSPrefetchDeferred = false; 453 // Else if prefetch was requested, clear flag and send cancellation 454 } else if (mDNSPrefetchRequested) { 455 mDNSPrefetchRequested = false; 456 // Possible that hostname could have changed since binding, but since this 457 // covers common cases, most DNS prefetch requests will be canceled 458 HTMLDNSPrefetch::CancelPrefetch( 459 *this, aOwner, HTMLDNSPrefetch::Priority::Low, NS_ERROR_ABORT); 460 } 461 } 462 463 DeferredDNSPrefetches::DeferredDNSPrefetches() 464 : mHead(0), mTail(0), mActiveLoaderCount(0), mTimerArmed(false) { 465 mTimer = NS_NewTimer(); 466 } 467 468 DeferredDNSPrefetches::~DeferredDNSPrefetches() { 469 if (mTimerArmed) { 470 mTimerArmed = false; 471 mTimer->Cancel(); 472 } 473 474 Flush(); 475 } 476 477 NS_IMPL_ISUPPORTS(DeferredDNSPrefetches, nsIWebProgressListener, 478 nsISupportsWeakReference, nsIObserver) 479 480 void DeferredDNSPrefetches::Flush() { 481 for (; mHead != mTail; mTail = (mTail + 1) & sMaxDeferredMask) { 482 Element* element = mEntries[mTail].mElement; 483 if (element) { 484 ToSupportsDNSPrefetch(*element).ClearIsInDNSPrefetch(); 485 } 486 mEntries[mTail].mElement = nullptr; 487 } 488 } 489 490 nsresult DeferredDNSPrefetches::Add(nsIDNSService::DNSFlags flags, 491 SupportsDNSPrefetch& aSupports, 492 Element& aElement) { 493 // The FIFO has no lock, so it can only be accessed on main thread 494 NS_ASSERTION(NS_IsMainThread(), 495 "DeferredDNSPrefetches::Add must be on main thread"); 496 497 aSupports.DNSPrefetchRequestDeferred(); 498 499 if (((mHead + 1) & sMaxDeferredMask) == mTail) { 500 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; 501 } 502 503 aSupports.SetIsInDNSPrefetch(); 504 mEntries[mHead].mFlags = flags; 505 mEntries[mHead].mElement = &aElement; 506 mHead = (mHead + 1) & sMaxDeferredMask; 507 508 if (!mActiveLoaderCount && !mTimerArmed && mTimer) { 509 mTimerArmed = true; 510 mTimer->InitWithNamedFuncCallback( 511 Tick, this, 2000, nsITimer::TYPE_ONE_SHOT, 512 "HTMLDNSPrefetch::DeferredDNSPrefetches::Tick"_ns); 513 } 514 515 return NS_OK; 516 } 517 518 void DeferredDNSPrefetches::SubmitQueue() { 519 NS_ASSERTION(NS_IsMainThread(), 520 "DeferredDNSPrefetches::SubmitQueue must be on main thread"); 521 if (!EnsureDNSService()) { 522 return; 523 } 524 525 for (; mHead != mTail; mTail = (mTail + 1) & sMaxDeferredMask) { 526 Element* element = mEntries[mTail].mElement; 527 if (!element) { 528 continue; 529 } 530 SubmitQueueEntry(*element, mEntries[mTail].mFlags); 531 mEntries[mTail].mElement = nullptr; 532 } 533 534 if (mTimerArmed) { 535 mTimerArmed = false; 536 mTimer->Cancel(); 537 } 538 } 539 540 void DeferredDNSPrefetches::SubmitQueueEntry(Element& aElement, 541 nsIDNSService::DNSFlags aFlags) { 542 auto& supports = ToSupportsDNSPrefetch(aElement); 543 supports.ClearIsInDNSPrefetch(); 544 545 // Only prefetch here if request was deferred and deferral not cancelled 546 if (!supports.IsDNSPrefetchRequestDeferred()) { 547 return; 548 } 549 550 HTMLDNSPrefetch::SendRequest(aElement, aFlags); 551 } 552 553 void DeferredDNSPrefetches::Activate() { 554 // Register as an observer for the document loader 555 nsCOMPtr<nsIWebProgress> progress = components::DocLoader::Service(); 556 if (progress) 557 progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT); 558 559 // Register as an observer for xpcom shutdown events so we can drop any 560 // element refs 561 nsCOMPtr<nsIObserverService> observerService = 562 mozilla::services::GetObserverService(); 563 if (observerService) 564 observerService->AddObserver(this, "xpcom-shutdown", true); 565 } 566 567 void DeferredDNSPrefetches::RemoveUnboundLinks() { 568 uint16_t tail = mTail; 569 while (mHead != tail) { 570 Element* element = mEntries[tail].mElement; 571 if (element && !element->IsInComposedDoc()) { 572 ToSupportsDNSPrefetch(*element).ClearIsInDNSPrefetch(); 573 mEntries[tail].mElement = nullptr; 574 } 575 tail = (tail + 1) & sMaxDeferredMask; 576 } 577 } 578 579 // nsITimer related method 580 581 void DeferredDNSPrefetches::Tick(nsITimer* aTimer, void* aClosure) { 582 auto* self = static_cast<DeferredDNSPrefetches*>(aClosure); 583 584 NS_ASSERTION(NS_IsMainThread(), 585 "DeferredDNSPrefetches::Tick must be on main thread"); 586 NS_ASSERTION(self->mTimerArmed, "Timer is not armed"); 587 588 self->mTimerArmed = false; 589 590 // If the queue is not submitted here because there are outstanding pages 591 // being loaded, there is no need to rearm the timer as the queue will be 592 // submtited when those loads complete. 593 if (!self->mActiveLoaderCount) { 594 self->SubmitQueue(); 595 } 596 } 597 598 //////////// nsIWebProgressListener methods 599 600 NS_IMETHODIMP 601 DeferredDNSPrefetches::OnStateChange(nsIWebProgress* aWebProgress, 602 nsIRequest* aRequest, 603 uint32_t progressStateFlags, 604 nsresult aStatus) { 605 // The FIFO has no lock, so it can only be accessed on main thread 606 NS_ASSERTION(NS_IsMainThread(), 607 "DeferredDNSPrefetches::OnStateChange must be on main thread"); 608 609 if (progressStateFlags & STATE_IS_DOCUMENT) { 610 if (progressStateFlags & STATE_STOP) { 611 // Initialization may have missed a STATE_START notification, so do 612 // not go negative 613 if (mActiveLoaderCount) mActiveLoaderCount--; 614 615 if (!mActiveLoaderCount) { 616 SubmitQueue(); 617 } 618 } else if (progressStateFlags & STATE_START) 619 mActiveLoaderCount++; 620 } 621 622 return NS_OK; 623 } 624 625 NS_IMETHODIMP 626 DeferredDNSPrefetches::OnProgressChange(nsIWebProgress* aProgress, 627 nsIRequest* aRequest, 628 int32_t curSelfProgress, 629 int32_t maxSelfProgress, 630 int32_t curTotalProgress, 631 int32_t maxTotalProgress) { 632 return NS_OK; 633 } 634 635 NS_IMETHODIMP 636 DeferredDNSPrefetches::OnLocationChange(nsIWebProgress* aWebProgress, 637 nsIRequest* aRequest, nsIURI* location, 638 uint32_t aFlags) { 639 return NS_OK; 640 } 641 642 NS_IMETHODIMP 643 DeferredDNSPrefetches::OnStatusChange(nsIWebProgress* aWebProgress, 644 nsIRequest* aRequest, nsresult aStatus, 645 const char16_t* aMessage) { 646 return NS_OK; 647 } 648 649 NS_IMETHODIMP 650 DeferredDNSPrefetches::OnSecurityChange(nsIWebProgress* aWebProgress, 651 nsIRequest* aRequest, uint32_t aState) { 652 return NS_OK; 653 } 654 655 NS_IMETHODIMP 656 DeferredDNSPrefetches::OnContentBlockingEvent(nsIWebProgress* aWebProgress, 657 nsIRequest* aRequest, 658 uint32_t aEvent) { 659 return NS_OK; 660 } 661 662 //////////// nsIObserver method 663 664 NS_IMETHODIMP 665 DeferredDNSPrefetches::Observe(nsISupports* subject, const char* topic, 666 const char16_t* data) { 667 if (!strcmp(topic, "xpcom-shutdown")) Flush(); 668 669 return NS_OK; 670 } 671 672 } // namespace mozilla::dom