Bug 1867190 - Initialise the PHC allocate delay later r=glandium
[gecko.git] / dom / html / HTMLDNSPrefetch.cpp
bloba4043195fed134f6bc364afcd29d577e83aa02be
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/. */
7 #include "HTMLDNSPrefetch.h"
9 #include "base/basictypes.h"
10 #include "mozilla/dom/Element.h"
11 #include "mozilla/dom/HTMLLinkElement.h"
12 #include "mozilla/dom/HTMLAnchorElement.h"
13 #include "mozilla/net/NeckoCommon.h"
14 #include "mozilla/net/NeckoChild.h"
15 #include "mozilla/OriginAttributes.h"
16 #include "mozilla/StoragePrincipalHelper.h"
17 #include "nsURLHelper.h"
19 #include "nsCOMPtr.h"
20 #include "nsString.h"
22 #include "nsNetUtil.h"
23 #include "nsNetCID.h"
24 #include "nsIProtocolHandler.h"
26 #include "nsIDNSListener.h"
27 #include "nsIWebProgressListener.h"
28 #include "nsIWebProgress.h"
29 #include "nsIDNSRecord.h"
30 #include "nsIDNSService.h"
31 #include "nsICancelable.h"
32 #include "nsGkAtoms.h"
33 #include "mozilla/dom/Document.h"
34 #include "nsThreadUtils.h"
35 #include "nsITimer.h"
36 #include "nsIObserverService.h"
38 #include "mozilla/Components.h"
39 #include "mozilla/Preferences.h"
40 #include "mozilla/StaticPrefs_network.h"
42 using namespace mozilla::net;
44 namespace mozilla::dom {
46 class NoOpDNSListener final : public nsIDNSListener {
47 // This class exists to give a safe callback no-op DNSListener
48 public:
49 NS_DECL_THREADSAFE_ISUPPORTS
50 NS_DECL_NSIDNSLISTENER
52 NoOpDNSListener() = default;
54 private:
55 ~NoOpDNSListener() = default;
58 NS_IMPL_ISUPPORTS(NoOpDNSListener, nsIDNSListener)
60 NS_IMETHODIMP
61 NoOpDNSListener::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
62 nsresult status) {
63 return NS_OK;
66 // This is just a (size) optimization and could be avoided by storing the
67 // SupportsDNSPrefetch pointer of the element in the prefetch queue, but given
68 // we need this for GetURIForDNSPrefetch...
69 static SupportsDNSPrefetch& ToSupportsDNSPrefetch(Element& aElement) {
70 if (auto* link = HTMLLinkElement::FromNode(aElement)) {
71 return *link;
73 auto* anchor = HTMLAnchorElement::FromNode(aElement);
74 MOZ_DIAGNOSTIC_ASSERT(anchor);
75 return *anchor;
78 nsIURI* SupportsDNSPrefetch::GetURIForDNSPrefetch(Element& aElement) {
79 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == this);
80 if (auto* link = HTMLLinkElement::FromNode(aElement)) {
81 return link->GetURI();
83 auto* anchor = HTMLAnchorElement::FromNode(aElement);
84 MOZ_DIAGNOSTIC_ASSERT(anchor);
85 return anchor->GetURI();
88 class DeferredDNSPrefetches final : public nsIWebProgressListener,
89 public nsSupportsWeakReference,
90 public nsIObserver {
91 public:
92 NS_DECL_ISUPPORTS
93 NS_DECL_NSIWEBPROGRESSLISTENER
94 NS_DECL_NSIOBSERVER
96 DeferredDNSPrefetches();
98 void Activate();
99 nsresult Add(nsIDNSService::DNSFlags flags, SupportsDNSPrefetch&, Element&);
101 void RemoveUnboundLinks();
103 private:
104 ~DeferredDNSPrefetches();
105 void Flush();
107 void SubmitQueue();
108 void SubmitQueueEntry(Element&, nsIDNSService::DNSFlags aFlags);
110 uint16_t mHead;
111 uint16_t mTail;
112 uint32_t mActiveLoaderCount;
114 nsCOMPtr<nsITimer> mTimer;
115 bool mTimerArmed;
116 static void Tick(nsITimer* aTimer, void* aClosure);
118 static const int sMaxDeferred = 512; // keep power of 2 for masking
119 static const int sMaxDeferredMask = (sMaxDeferred - 1);
121 struct deferred_entry {
122 nsIDNSService::DNSFlags mFlags;
123 // SupportsDNSPrefetch clears this raw pointer in Destroyed().
124 Element* mElement;
125 } mEntries[sMaxDeferred];
128 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
129 static bool sInitialized = false;
130 static nsIDNSService* sDNSService = nullptr;
131 static DeferredDNSPrefetches* sPrefetches = nullptr;
132 static NoOpDNSListener* sDNSListener = nullptr;
134 nsresult HTMLDNSPrefetch::Initialize() {
135 if (sInitialized) {
136 NS_WARNING("Initialize() called twice");
137 return NS_OK;
140 sPrefetches = new DeferredDNSPrefetches();
141 NS_ADDREF(sPrefetches);
143 sDNSListener = new NoOpDNSListener();
144 NS_ADDREF(sDNSListener);
146 sPrefetches->Activate();
148 if (IsNeckoChild()) NeckoChild::InitNeckoChild();
150 sInitialized = true;
151 return NS_OK;
154 nsresult HTMLDNSPrefetch::Shutdown() {
155 if (!sInitialized) {
156 NS_WARNING("Not Initialized");
157 return NS_OK;
159 sInitialized = false;
160 NS_IF_RELEASE(sDNSService);
161 NS_IF_RELEASE(sPrefetches);
162 NS_IF_RELEASE(sDNSListener);
164 return NS_OK;
167 static bool EnsureDNSService() {
168 if (sDNSService) {
169 return true;
172 NS_IF_RELEASE(sDNSService);
173 nsresult rv;
174 rv = CallGetService(kDNSServiceCID, &sDNSService);
175 if (NS_FAILED(rv)) {
176 return false;
179 return !!sDNSService;
182 bool HTMLDNSPrefetch::IsAllowed(Document* aDocument) {
183 // There is no need to do prefetch on non UI scenarios such as XMLHttpRequest.
184 return aDocument->IsDNSPrefetchAllowed() && aDocument->GetWindow();
187 static nsIDNSService::DNSFlags GetDNSFlagsFromElement(Element& aElement) {
188 nsIChannel* channel = aElement.OwnerDoc()->GetChannel();
189 if (!channel) {
190 return nsIDNSService::RESOLVE_DEFAULT_FLAGS;
192 return nsIDNSService::GetFlagsFromTRRMode(channel->GetTRRMode());
195 nsIDNSService::DNSFlags HTMLDNSPrefetch::PriorityToDNSServiceFlags(
196 Priority aPriority) {
197 switch (aPriority) {
198 case Priority::Low:
199 return nsIDNSService::RESOLVE_PRIORITY_LOW;
200 case Priority::Medium:
201 return nsIDNSService::RESOLVE_PRIORITY_MEDIUM;
202 case Priority::High:
203 return nsIDNSService::RESOLVE_DEFAULT_FLAGS;
205 MOZ_ASSERT_UNREACHABLE("Unknown priority");
206 return nsIDNSService::RESOLVE_DEFAULT_FLAGS;
209 nsresult HTMLDNSPrefetch::Prefetch(SupportsDNSPrefetch& aSupports,
210 Element& aElement, Priority aPriority) {
211 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == &aSupports);
212 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService()) {
213 return NS_ERROR_NOT_AVAILABLE;
215 return sPrefetches->Add(
216 GetDNSFlagsFromElement(aElement) | PriorityToDNSServiceFlags(aPriority),
217 aSupports, aElement);
220 nsresult HTMLDNSPrefetch::Prefetch(
221 const nsAString& hostname, bool isHttps,
222 const OriginAttributes& aPartitionedPrincipalOriginAttributes,
223 nsIDNSService::DNSFlags flags) {
224 if (IsNeckoChild()) {
225 // We need to check IsEmpty() because net_IsValidHostName()
226 // considers empty strings to be valid hostnames
227 if (!hostname.IsEmpty() &&
228 net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) {
229 // during shutdown gNeckoChild might be null
230 if (gNeckoChild) {
231 gNeckoChild->SendHTMLDNSPrefetch(
232 hostname, isHttps, aPartitionedPrincipalOriginAttributes, flags);
235 return NS_OK;
238 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService())
239 return NS_ERROR_NOT_AVAILABLE;
241 nsCOMPtr<nsICancelable> tmpOutstanding;
242 nsresult rv = sDNSService->AsyncResolveNative(
243 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_DEFAULT,
244 flags | nsIDNSService::RESOLVE_SPECULATE, nullptr, sDNSListener, nullptr,
245 aPartitionedPrincipalOriginAttributes, getter_AddRefs(tmpOutstanding));
246 if (NS_FAILED(rv)) {
247 return rv;
250 if (StaticPrefs::network_dns_upgrade_with_https_rr() ||
251 StaticPrefs::network_dns_use_https_rr_as_altsvc()) {
252 Unused << sDNSService->AsyncResolveNative(
253 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
254 flags | nsIDNSService::RESOLVE_SPECULATE, nullptr, sDNSListener,
255 nullptr, aPartitionedPrincipalOriginAttributes,
256 getter_AddRefs(tmpOutstanding));
259 return NS_OK;
262 nsresult HTMLDNSPrefetch::Prefetch(
263 const nsAString& hostname, bool isHttps,
264 const OriginAttributes& aPartitionedPrincipalOriginAttributes,
265 nsIRequest::TRRMode aMode, Priority aPriority) {
266 return Prefetch(hostname, isHttps, aPartitionedPrincipalOriginAttributes,
267 nsIDNSService::GetFlagsFromTRRMode(aMode) |
268 PriorityToDNSServiceFlags(aPriority));
271 nsresult HTMLDNSPrefetch::CancelPrefetch(SupportsDNSPrefetch& aSupports,
272 Element& aElement, Priority aPriority,
273 nsresult aReason) {
274 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == &aSupports);
276 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService()) {
277 return NS_ERROR_NOT_AVAILABLE;
280 nsIDNSService::DNSFlags flags =
281 GetDNSFlagsFromElement(aElement) | PriorityToDNSServiceFlags(aPriority);
283 nsIURI* uri = aSupports.GetURIForDNSPrefetch(aElement);
284 if (!uri) {
285 return NS_OK;
288 nsAutoCString hostname;
289 uri->GetAsciiHost(hostname);
291 nsAutoString protocol;
292 bool isHttps = uri->SchemeIs("https");
294 OriginAttributes oa;
295 StoragePrincipalHelper::GetOriginAttributesForNetworkState(
296 aElement.OwnerDoc(), oa);
298 return CancelPrefetch(NS_ConvertUTF8toUTF16(hostname), isHttps, oa, flags,
299 aReason);
302 nsresult HTMLDNSPrefetch::CancelPrefetch(
303 const nsAString& hostname, bool isHttps,
304 const OriginAttributes& aPartitionedPrincipalOriginAttributes,
305 nsIDNSService::DNSFlags flags, nsresult aReason) {
306 // Forward this request to Necko Parent if we're a child process
307 if (IsNeckoChild()) {
308 // We need to check IsEmpty() because net_IsValidHostName()
309 // considers empty strings to be valid hostnames
310 if (!hostname.IsEmpty() &&
311 net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) {
312 // during shutdown gNeckoChild might be null
313 if (gNeckoChild) {
314 gNeckoChild->SendCancelHTMLDNSPrefetch(
315 hostname, isHttps, aPartitionedPrincipalOriginAttributes, flags,
316 aReason);
319 return NS_OK;
322 if (!(sInitialized && sPrefetches && sDNSListener) || !EnsureDNSService()) {
323 return NS_ERROR_NOT_AVAILABLE;
326 // Forward cancellation to DNS service
327 nsresult rv = sDNSService->CancelAsyncResolveNative(
328 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_DEFAULT,
329 flags | nsIDNSService::RESOLVE_SPECULATE,
330 nullptr, // AdditionalInfo
331 sDNSListener, aReason, aPartitionedPrincipalOriginAttributes);
333 if (StaticPrefs::network_dns_upgrade_with_https_rr() ||
334 StaticPrefs::network_dns_use_https_rr_as_altsvc()) {
335 Unused << sDNSService->CancelAsyncResolveNative(
336 NS_ConvertUTF16toUTF8(hostname), nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
337 flags | nsIDNSService::RESOLVE_SPECULATE,
338 nullptr, // AdditionalInfo
339 sDNSListener, aReason, aPartitionedPrincipalOriginAttributes);
341 return rv;
344 nsresult HTMLDNSPrefetch::CancelPrefetch(
345 const nsAString& hostname, bool isHttps,
346 const OriginAttributes& aPartitionedPrincipalOriginAttributes,
347 nsIRequest::TRRMode aTRRMode, Priority aPriority, nsresult aReason) {
348 return CancelPrefetch(hostname, isHttps,
349 aPartitionedPrincipalOriginAttributes,
350 nsIDNSService::GetFlagsFromTRRMode(aTRRMode) |
351 PriorityToDNSServiceFlags(aPriority),
352 aReason);
355 void HTMLDNSPrefetch::ElementDestroyed(Element& aElement,
356 SupportsDNSPrefetch& aSupports) {
357 MOZ_ASSERT(&ToSupportsDNSPrefetch(aElement) == &aSupports);
358 MOZ_ASSERT(aSupports.IsInDNSPrefetch());
359 if (sPrefetches) {
360 // Clean up all the possible links at once.
361 sPrefetches->RemoveUnboundLinks();
365 void SupportsDNSPrefetch::TryDNSPrefetch(Element& aOwner) {
366 MOZ_ASSERT(aOwner.IsInComposedDoc());
367 if (HTMLDNSPrefetch::IsAllowed(aOwner.OwnerDoc())) {
368 HTMLDNSPrefetch::Prefetch(*this, aOwner, HTMLDNSPrefetch::Priority::Low);
372 void SupportsDNSPrefetch::CancelDNSPrefetch(Element& aOwner) {
373 // If prefetch was deferred, clear flag and move on
374 if (mDNSPrefetchDeferred) {
375 mDNSPrefetchDeferred = false;
376 // Else if prefetch was requested, clear flag and send cancellation
377 } else if (mDNSPrefetchRequested) {
378 mDNSPrefetchRequested = false;
379 // Possible that hostname could have changed since binding, but since this
380 // covers common cases, most DNS prefetch requests will be canceled
381 HTMLDNSPrefetch::CancelPrefetch(
382 *this, aOwner, HTMLDNSPrefetch::Priority::Low, NS_ERROR_ABORT);
386 DeferredDNSPrefetches::DeferredDNSPrefetches()
387 : mHead(0), mTail(0), mActiveLoaderCount(0), mTimerArmed(false) {
388 mTimer = NS_NewTimer();
391 DeferredDNSPrefetches::~DeferredDNSPrefetches() {
392 if (mTimerArmed) {
393 mTimerArmed = false;
394 mTimer->Cancel();
397 Flush();
400 NS_IMPL_ISUPPORTS(DeferredDNSPrefetches, nsIWebProgressListener,
401 nsISupportsWeakReference, nsIObserver)
403 void DeferredDNSPrefetches::Flush() {
404 for (; mHead != mTail; mTail = (mTail + 1) & sMaxDeferredMask) {
405 Element* element = mEntries[mTail].mElement;
406 if (element) {
407 ToSupportsDNSPrefetch(*element).ClearIsInDNSPrefetch();
409 mEntries[mTail].mElement = nullptr;
413 nsresult DeferredDNSPrefetches::Add(nsIDNSService::DNSFlags flags,
414 SupportsDNSPrefetch& aSupports,
415 Element& aElement) {
416 // The FIFO has no lock, so it can only be accessed on main thread
417 NS_ASSERTION(NS_IsMainThread(),
418 "DeferredDNSPrefetches::Add must be on main thread");
420 aSupports.DNSPrefetchRequestDeferred();
422 if (((mHead + 1) & sMaxDeferredMask) == mTail) {
423 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
426 aSupports.SetIsInDNSPrefetch();
427 mEntries[mHead].mFlags = flags;
428 mEntries[mHead].mElement = &aElement;
429 mHead = (mHead + 1) & sMaxDeferredMask;
431 if (!mActiveLoaderCount && !mTimerArmed && mTimer) {
432 mTimerArmed = true;
433 mTimer->InitWithNamedFuncCallback(
434 Tick, this, 2000, nsITimer::TYPE_ONE_SHOT,
435 "HTMLDNSPrefetch::DeferredDNSPrefetches::Tick");
438 return NS_OK;
441 void DeferredDNSPrefetches::SubmitQueue() {
442 NS_ASSERTION(NS_IsMainThread(),
443 "DeferredDNSPrefetches::SubmitQueue must be on main thread");
444 if (!EnsureDNSService()) {
445 return;
448 for (; mHead != mTail; mTail = (mTail + 1) & sMaxDeferredMask) {
449 Element* element = mEntries[mTail].mElement;
450 if (!element) {
451 continue;
453 SubmitQueueEntry(*element, mEntries[mTail].mFlags);
454 mEntries[mTail].mElement = nullptr;
457 if (mTimerArmed) {
458 mTimerArmed = false;
459 mTimer->Cancel();
463 void DeferredDNSPrefetches::SubmitQueueEntry(Element& aElement,
464 nsIDNSService::DNSFlags aFlags) {
465 auto& supports = ToSupportsDNSPrefetch(aElement);
466 supports.ClearIsInDNSPrefetch();
468 // Only prefetch here if request was deferred and deferral not cancelled
469 if (!supports.IsDNSPrefetchRequestDeferred()) {
470 return;
473 nsIURI* uri = supports.GetURIForDNSPrefetch(aElement);
474 if (!uri) {
475 return;
478 nsAutoCString hostName;
479 uri->GetAsciiHost(hostName);
480 if (hostName.IsEmpty()) {
481 return;
484 bool isLocalResource = false;
485 nsresult rv = NS_URIChainHasFlags(
486 uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &isLocalResource);
487 if (NS_FAILED(rv) || isLocalResource) {
488 return;
491 OriginAttributes oa;
492 StoragePrincipalHelper::GetOriginAttributesForNetworkState(
493 aElement.OwnerDoc(), oa);
495 bool isHttps = uri->SchemeIs("https");
497 if (IsNeckoChild()) {
498 // during shutdown gNeckoChild might be null
499 if (gNeckoChild) {
500 gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), isHttps,
501 oa, mEntries[mTail].mFlags);
503 } else {
504 nsCOMPtr<nsICancelable> tmpOutstanding;
506 rv = sDNSService->AsyncResolveNative(
507 hostName, nsIDNSService::RESOLVE_TYPE_DEFAULT,
508 mEntries[mTail].mFlags | nsIDNSService::RESOLVE_SPECULATE, nullptr,
509 sDNSListener, nullptr, oa, getter_AddRefs(tmpOutstanding));
510 if (NS_FAILED(rv)) {
511 return;
514 // Fetch HTTPS RR if needed.
515 if (StaticPrefs::network_dns_upgrade_with_https_rr() ||
516 StaticPrefs::network_dns_use_https_rr_as_altsvc()) {
517 sDNSService->AsyncResolveNative(
518 hostName, nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
519 mEntries[mTail].mFlags | nsIDNSService::RESOLVE_SPECULATE, nullptr,
520 sDNSListener, nullptr, oa, getter_AddRefs(tmpOutstanding));
524 // Tell element that deferred prefetch was requested.
525 supports.DNSPrefetchRequestStarted();
528 void DeferredDNSPrefetches::Activate() {
529 // Register as an observer for the document loader
530 nsCOMPtr<nsIWebProgress> progress = components::DocLoader::Service();
531 if (progress)
532 progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
534 // Register as an observer for xpcom shutdown events so we can drop any
535 // element refs
536 nsCOMPtr<nsIObserverService> observerService =
537 mozilla::services::GetObserverService();
538 if (observerService)
539 observerService->AddObserver(this, "xpcom-shutdown", true);
542 void DeferredDNSPrefetches::RemoveUnboundLinks() {
543 uint16_t tail = mTail;
544 while (mHead != tail) {
545 Element* element = mEntries[tail].mElement;
546 if (element && !element->IsInComposedDoc()) {
547 ToSupportsDNSPrefetch(*element).ClearIsInDNSPrefetch();
548 mEntries[tail].mElement = nullptr;
550 tail = (tail + 1) & sMaxDeferredMask;
554 // nsITimer related method
556 void DeferredDNSPrefetches::Tick(nsITimer* aTimer, void* aClosure) {
557 auto* self = static_cast<DeferredDNSPrefetches*>(aClosure);
559 NS_ASSERTION(NS_IsMainThread(),
560 "DeferredDNSPrefetches::Tick must be on main thread");
561 NS_ASSERTION(self->mTimerArmed, "Timer is not armed");
563 self->mTimerArmed = false;
565 // If the queue is not submitted here because there are outstanding pages
566 // being loaded, there is no need to rearm the timer as the queue will be
567 // submtited when those loads complete.
568 if (!self->mActiveLoaderCount) {
569 self->SubmitQueue();
573 //////////// nsIWebProgressListener methods
575 NS_IMETHODIMP
576 DeferredDNSPrefetches::OnStateChange(nsIWebProgress* aWebProgress,
577 nsIRequest* aRequest,
578 uint32_t progressStateFlags,
579 nsresult aStatus) {
580 // The FIFO has no lock, so it can only be accessed on main thread
581 NS_ASSERTION(NS_IsMainThread(),
582 "DeferredDNSPrefetches::OnStateChange must be on main thread");
584 if (progressStateFlags & STATE_IS_DOCUMENT) {
585 if (progressStateFlags & STATE_STOP) {
586 // Initialization may have missed a STATE_START notification, so do
587 // not go negative
588 if (mActiveLoaderCount) mActiveLoaderCount--;
590 if (!mActiveLoaderCount) {
591 SubmitQueue();
593 } else if (progressStateFlags & STATE_START)
594 mActiveLoaderCount++;
597 return NS_OK;
600 NS_IMETHODIMP
601 DeferredDNSPrefetches::OnProgressChange(nsIWebProgress* aProgress,
602 nsIRequest* aRequest,
603 int32_t curSelfProgress,
604 int32_t maxSelfProgress,
605 int32_t curTotalProgress,
606 int32_t maxTotalProgress) {
607 return NS_OK;
610 NS_IMETHODIMP
611 DeferredDNSPrefetches::OnLocationChange(nsIWebProgress* aWebProgress,
612 nsIRequest* aRequest, nsIURI* location,
613 uint32_t aFlags) {
614 return NS_OK;
617 NS_IMETHODIMP
618 DeferredDNSPrefetches::OnStatusChange(nsIWebProgress* aWebProgress,
619 nsIRequest* aRequest, nsresult aStatus,
620 const char16_t* aMessage) {
621 return NS_OK;
624 NS_IMETHODIMP
625 DeferredDNSPrefetches::OnSecurityChange(nsIWebProgress* aWebProgress,
626 nsIRequest* aRequest, uint32_t aState) {
627 return NS_OK;
630 NS_IMETHODIMP
631 DeferredDNSPrefetches::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
632 nsIRequest* aRequest,
633 uint32_t aEvent) {
634 return NS_OK;
637 //////////// nsIObserver method
639 NS_IMETHODIMP
640 DeferredDNSPrefetches::Observe(nsISupports* subject, const char* topic,
641 const char16_t* data) {
642 if (!strcmp(topic, "xpcom-shutdown")) Flush();
644 return NS_OK;
647 } // namespace mozilla::dom