tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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