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