Bug 1572526 - Move a bunch of code that's only called by HTMLLinkElement out of Link.cpp. r=bzbarsky
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 15 Aug 2019 14:10:08 +0000
changeset 488236 31defc04f73a3e33c4b279fc317953614c953cbb
parent 488235 b3194a6ba51e91aefe3933da163e6ba622b0c9f7
child 488237 e8700fab7cb87b760e78b469ff48d7c731092adb
push id113904
push userncsoregi@mozilla.com
push dateThu, 15 Aug 2019 19:41:00 +0000
treeherdermozilla-inbound@b283a7ef186c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1572526
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1572526 - Move a bunch of code that's only called by HTMLLinkElement out of Link.cpp. r=bzbarsky Seems like a more logical place for it to live. Differential Revision: https://phabricator.services.mozilla.com/D41270
dom/base/Link.cpp
dom/base/Link.h
dom/base/nsContentSink.cpp
dom/html/HTMLLinkElement.cpp
dom/html/HTMLLinkElement.h
--- a/dom/base/Link.cpp
+++ b/dom/base/Link.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Link.h"
 
-#include "mozilla/Components.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
 #if defined(MOZ_PLACES)
 #  include "mozilla/places/History.h"
 #else
 #  include "mozilla/IHistory.h"
 #endif
@@ -90,246 +89,16 @@ void Link::CancelDNSPrefetch(nsWrapperCa
   } else if (mElement->HasFlag(aRequestedFlag)) {
     mElement->UnsetFlags(aRequestedFlag);
     // Possible that hostname could have changed since binding, but since this
     // covers common cases, most DNS prefetch requests will be canceled
     nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
   }
 }
 
-void Link::GetContentPolicyMimeTypeMedia(nsAttrValue& aAsAttr,
-                                         nsContentPolicyType& aPolicyType,
-                                         nsString& aMimeType,
-                                         nsAString& aMedia) {
-  nsAutoString as;
-  mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::as, as);
-  Link::ParseAsValue(as, aAsAttr);
-  aPolicyType = AsValueToContentPolicy(aAsAttr);
-
-  nsAutoString type;
-  mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
-  nsAutoString notUsed;
-  nsContentUtils::SplitMimeType(type, aMimeType, notUsed);
-
-  mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia);
-}
-
-void Link::TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender() {
-  MOZ_ASSERT(mElement->IsInComposedDoc());
-  if (!ElementHasHref()) {
-    return;
-  }
-
-  nsAutoString rel;
-  if (!mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
-    return;
-  }
-
-  if (!nsContentUtils::PrefetchPreloadEnabled(
-          mElement->OwnerDoc()->GetDocShell())) {
-    return;
-  }
-
-  uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel);
-
-  if ((linkTypes & nsStyleLinkElement::ePREFETCH) ||
-      (linkTypes & nsStyleLinkElement::eNEXT) ||
-      (linkTypes & nsStyleLinkElement::ePRELOAD)) {
-    nsCOMPtr<nsIPrefetchService> prefetchService(
-        components::Prefetch::Service());
-    if (prefetchService) {
-      nsCOMPtr<nsIURI> uri(GetURI());
-      if (uri) {
-        bool preload = !!(linkTypes & nsStyleLinkElement::ePRELOAD);
-        nsContentPolicyType policyType;
-
-        if (preload) {
-          nsAttrValue asAttr;
-          nsAutoString mimeType;
-          nsAutoString media;
-          GetContentPolicyMimeTypeMedia(asAttr, policyType, mimeType, media);
-
-          if (policyType == nsIContentPolicy::TYPE_INVALID) {
-            // Ignore preload with a wrong or empty as attribute.
-            return;
-          }
-
-          if (!HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, media,
-                                                  mElement->OwnerDoc())) {
-            policyType = nsIContentPolicy::TYPE_INVALID;
-          }
-        }
-
-        nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
-        referrerInfo->InitWithNode(mElement);
-        if (preload) {
-          prefetchService->PreloadURI(uri, referrerInfo, mElement, policyType);
-        } else {
-          prefetchService->PrefetchURI(
-              uri, referrerInfo, mElement,
-              linkTypes & nsStyleLinkElement::ePREFETCH);
-        }
-        return;
-      }
-    }
-  }
-
-  if (linkTypes & nsStyleLinkElement::ePRECONNECT) {
-    nsCOMPtr<nsIURI> uri(GetURI());
-    if (uri && mElement->OwnerDoc()) {
-      mElement->OwnerDoc()->MaybePreconnect(
-          uri, Element::AttrValueToCORSMode(
-                   mElement->GetParsedAttr(nsGkAtoms::crossorigin)));
-      return;
-    }
-  }
-
-  if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) {
-    if (nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) {
-      nsHTMLDNSPrefetch::PrefetchLow(this);
-    }
-  }
-}
-
-void Link::UpdatePreload(nsAtom* aName, const nsAttrValue* aValue,
-                         const nsAttrValue* aOldValue) {
-  MOZ_ASSERT(mElement->IsInComposedDoc());
-
-  if (!ElementHasHref()) {
-    return;
-  }
-
-  nsAutoString rel;
-  if (!mElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
-    return;
-  }
-
-  if (!nsContentUtils::PrefetchPreloadEnabled(
-          mElement->OwnerDoc()->GetDocShell())) {
-    return;
-  }
-
-  uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel);
-
-  if (!(linkTypes & nsStyleLinkElement::ePRELOAD)) {
-    return;
-  }
-
-  nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service());
-  if (!prefetchService) {
-    return;
-  }
-
-  nsCOMPtr<nsIURI> uri(GetURI());
-  if (!uri) {
-    return;
-  }
-
-  nsAttrValue asAttr;
-  nsContentPolicyType asPolicyType;
-  nsAutoString mimeType;
-  nsAutoString media;
-  GetContentPolicyMimeTypeMedia(asAttr, asPolicyType, mimeType, media);
-
-  if (asPolicyType == nsIContentPolicy::TYPE_INVALID) {
-    // Ignore preload with a wrong or empty as attribute, but be sure to cancel
-    // the old one.
-    prefetchService->CancelPrefetchPreloadURI(uri, mElement);
-    return;
-  }
-
-  nsContentPolicyType policyType = asPolicyType;
-  if (!HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, media,
-                                          mElement->OwnerDoc())) {
-    policyType = nsIContentPolicy::TYPE_INVALID;
-  }
-
-  if (aName == nsGkAtoms::crossorigin) {
-    CORSMode corsMode = Element::AttrValueToCORSMode(aValue);
-    CORSMode oldCorsMode = Element::AttrValueToCORSMode(aOldValue);
-    if (corsMode != oldCorsMode) {
-      prefetchService->CancelPrefetchPreloadURI(uri, mElement);
-
-      nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
-      referrerInfo->InitWithNode(mElement);
-      prefetchService->PreloadURI(uri, referrerInfo, mElement, policyType);
-    }
-    return;
-  }
-
-  nsContentPolicyType oldPolicyType;
-
-  if (aName == nsGkAtoms::as) {
-    if (aOldValue) {
-      oldPolicyType = AsValueToContentPolicy(*aOldValue);
-      if (!HTMLLinkElement::CheckPreloadAttrs(*aOldValue, mimeType, media,
-                                              mElement->OwnerDoc())) {
-        oldPolicyType = nsIContentPolicy::TYPE_INVALID;
-      }
-    } else {
-      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
-    }
-  } else if (aName == nsGkAtoms::type) {
-    nsAutoString oldType;
-    nsAutoString notUsed;
-    if (aOldValue) {
-      aOldValue->ToString(oldType);
-    } else {
-      oldType = EmptyString();
-    }
-    nsAutoString oldMimeType;
-    nsContentUtils::SplitMimeType(oldType, oldMimeType, notUsed);
-    if (HTMLLinkElement::CheckPreloadAttrs(asAttr, oldMimeType, media,
-                                           mElement->OwnerDoc())) {
-      oldPolicyType = asPolicyType;
-    } else {
-      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
-    }
-  } else {
-    MOZ_ASSERT(aName == nsGkAtoms::media);
-    nsAutoString oldMedia;
-    if (aOldValue) {
-      aOldValue->ToString(oldMedia);
-    } else {
-      oldMedia = EmptyString();
-    }
-    if (HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, oldMedia,
-                                           mElement->OwnerDoc())) {
-      oldPolicyType = asPolicyType;
-    } else {
-      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
-    }
-  }
-
-  if ((policyType != oldPolicyType) &&
-      (oldPolicyType != nsIContentPolicy::TYPE_INVALID)) {
-    prefetchService->CancelPrefetchPreloadURI(uri, mElement);
-  }
-
-  // Trigger a new preload if the policy type has changed.
-  // Also trigger load if the new policy type is invalid, this will only
-  // trigger an error event.
-  if ((policyType != oldPolicyType) ||
-      (policyType == nsIContentPolicy::TYPE_INVALID)) {
-    nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
-    referrerInfo->InitWithNode(mElement);
-    prefetchService->PreloadURI(uri, referrerInfo, mElement, policyType);
-  }
-}
-
-void Link::CancelPrefetchOrPreload() {
-  nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service());
-  if (prefetchService) {
-    nsCOMPtr<nsIURI> uri(GetURI());
-    if (uri) {
-      prefetchService->CancelPrefetchPreloadURI(uri, mElement);
-    }
-  }
-}
-
 void Link::SetLinkState(nsLinkState aState) {
   MOZ_ASSERT(mRegistered, "Setting the link state of an unregistered Link!");
   MOZ_ASSERT(mLinkState != aState, "Setting state to the currently set state!");
 
   // Set our current state as appropriate.
   mLinkState = aState;
 
   // Per IHistory interface documentation, we are no longer registered.
@@ -830,53 +599,10 @@ size_t Link::SizeOfExcludingThis(mozilla
   }
 
   // The following members don't need to be measured:
   // - mElement, because it is a pointer-to-self used to avoid QIs
 
   return n;
 }
 
-static const nsAttrValue::EnumTable kAsAttributeTable[] = {
-    {"", DESTINATION_INVALID},      {"audio", DESTINATION_AUDIO},
-    {"font", DESTINATION_FONT},     {"image", DESTINATION_IMAGE},
-    {"script", DESTINATION_SCRIPT}, {"style", DESTINATION_STYLE},
-    {"track", DESTINATION_TRACK},   {"video", DESTINATION_VIDEO},
-    {"fetch", DESTINATION_FETCH},   {nullptr, 0}};
-
-/* static */
-void Link::ParseAsValue(const nsAString& aValue, nsAttrValue& aResult) {
-  DebugOnly<bool> success =
-      aResult.ParseEnumValue(aValue, kAsAttributeTable, false,
-                             // default value is a empty string
-                             // if aValue is not a value we
-                             // understand
-                             &kAsAttributeTable[0]);
-  MOZ_ASSERT(success);
-}
-
-/* static */
-nsContentPolicyType Link::AsValueToContentPolicy(const nsAttrValue& aValue) {
-  switch (aValue.GetEnumValue()) {
-    case DESTINATION_INVALID:
-      return nsIContentPolicy::TYPE_INVALID;
-    case DESTINATION_AUDIO:
-      return nsIContentPolicy::TYPE_INTERNAL_AUDIO;
-    case DESTINATION_TRACK:
-      return nsIContentPolicy::TYPE_INTERNAL_TRACK;
-    case DESTINATION_VIDEO:
-      return nsIContentPolicy::TYPE_INTERNAL_VIDEO;
-    case DESTINATION_FONT:
-      return nsIContentPolicy::TYPE_FONT;
-    case DESTINATION_IMAGE:
-      return nsIContentPolicy::TYPE_IMAGE;
-    case DESTINATION_SCRIPT:
-      return nsIContentPolicy::TYPE_SCRIPT;
-    case DESTINATION_STYLE:
-      return nsIContentPolicy::TYPE_STYLESHEET;
-    case DESTINATION_FETCH:
-      return nsIContentPolicy::TYPE_OTHER;
-  }
-  return nsIContentPolicy::TYPE_INVALID;
-}
-
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/Link.h
+++ b/dom/base/Link.h
@@ -114,44 +114,35 @@ class Link : public nsISupports {
    *          Defaults to true; should be overridden for specialised cases
    */
   virtual bool HasDeferredDNSPrefetchRequest() { return true; }
 
   virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) const;
 
   virtual bool ElementHasHref() const;
 
-  // This is called by HTMLAnchorElement.
+  // This is called by HTMLAnchorElement and HTMLLinkElement.
   void TryDNSPrefetch();
   void CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
                          nsWrapperCache::FlagsType aRequestedFlag);
 
-  // This is called by HTMLLinkElement.
-  void TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
-  void UpdatePreload(nsAtom* aName, const nsAttrValue* aValue,
-                     const nsAttrValue* aOldValue);
-  void CancelPrefetchOrPreload();
-
   bool HasPendingLinkUpdate() const { return mHasPendingLinkUpdate; }
   void SetHasPendingLinkUpdate() { mHasPendingLinkUpdate = true; }
   void ClearHasPendingLinkUpdate() { mHasPendingLinkUpdate = false; }
 
   // To ensure correct mHasPendingLinkUpdate handling, we have this method
   // similar to the one in Element. Overriders must call
   // ClearHasPendingLinkUpdate().
   // If you change this, change also the method in Element.
   virtual void NodeInfoChanged(Document* aOldDoc) = 0;
 
   bool IsInDNSPrefetch() { return mInDNSPrefetch; }
   void SetIsInDNSPrefetch() { mInDNSPrefetch = true; }
   void ClearIsInDNSPrefetch() { mInDNSPrefetch = false; }
 
-  static void ParseAsValue(const nsAString& aValue, nsAttrValue& aResult);
-  static nsContentPolicyType AsValueToContentPolicy(const nsAttrValue& aValue);
-
  protected:
   virtual ~Link();
 
   /**
    * Return true if the link has associated URI.
    */
   bool HasURI() const {
     if (HasCachedURI()) {
@@ -168,20 +159,16 @@ class Link : public nsISupports {
   /**
    * Unregisters from History so this node no longer gets notifications about
    * changes to visitedness.
    */
   void UnregisterFromHistory();
 
   void SetHrefAttribute(nsIURI* aURI);
 
-  void GetContentPolicyMimeTypeMedia(nsAttrValue& aAsAttr,
-                                     nsContentPolicyType& aPolicyType,
-                                     nsString& aMimeType, nsAString& aMedia);
-
   mutable nsCOMPtr<nsIURI> mCachedURI;
 
   Element* const mElement;
 
   uint16_t mLinkState;
 
   bool mNeedsRegistration : 1;
 
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -795,18 +795,18 @@ void nsContentSink::PrefetchPreloadHref(
     nsCOMPtr<nsIURI> uri;
     NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
     if (uri) {
       bool preload = !!(aLinkTypes & nsStyleLinkElement::ePRELOAD);
       nsContentPolicyType policyType;
 
       if (preload) {
         nsAttrValue asAttr;
-        Link::ParseAsValue(aAs, asAttr);
-        policyType = Link::AsValueToContentPolicy(asAttr);
+        HTMLLinkElement::ParseAsValue(aAs, asAttr);
+        policyType = HTMLLinkElement::AsValueToContentPolicy(asAttr);
 
         if (policyType == nsIContentPolicy::TYPE_INVALID) {
           // Ignore preload with a wrong or empty as attribute.
           return;
         }
 
         nsAutoString mimeType;
         nsAutoString notUsed;
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -3,30 +3,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLLinkElement.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/Components.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindContext.h"
 #include "mozilla/dom/DocumentInlines.h"
 #include "mozilla/dom/HTMLLinkElementBinding.h"
 #include "nsContentUtils.h"
+#include "nsDOMTokenList.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
-#include "nsDOMTokenList.h"
+#include "nsHTMLDNSPrefetch.h"
 #include "nsIContentInlines.h"
 #include "mozilla/dom/Document.h"
 #include "nsINode.h"
+#include "nsIPrefetchService.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsIURL.h"
 #include "nsPIDOMWindow.h"
 #include "nsReadableUtils.h"
 #include "nsStyleConsts.h"
 #include "nsStyleLinkElement.h"
 #include "nsUnicharUtils.h"
 #include "nsWindowSizes.h"
@@ -321,19 +324,19 @@ nsresult HTMLLinkElement::AfterSetAttr(i
          aName == nsGkAtoms::type || aName == nsGkAtoms::as ||
          aName == nsGkAtoms::crossorigin ||
          (aName == nsGkAtoms::disabled &&
           StaticPrefs::dom_link_disabled_attribute_enabled()))) {
       bool dropSheet = false;
       if (aName == nsGkAtoms::rel) {
         nsAutoString value;
         aValue->ToString(value);
-        uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(value);
+        uint32_t linkTypes = ParseLinkTypes(value);
         if (GetSheet()) {
-          dropSheet = !(linkTypes & nsStyleLinkElement::eSTYLESHEET);
+          dropSheet = !(linkTypes & eSTYLESHEET);
         }
       }
 
       if ((aName == nsGkAtoms::rel || aName == nsGkAtoms::href) &&
           IsInComposedDoc()) {
         TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
       }
 
@@ -414,34 +417,34 @@ nsDOMTokenList* HTMLLinkElement::RelList
 
 already_AddRefed<nsIURI> HTMLLinkElement::GetHrefURI() const {
   return GetHrefURIForAnchors();
 }
 
 Maybe<nsStyleLinkElement::SheetInfo> HTMLLinkElement::GetStyleSheetInfo() {
   nsAutoString rel;
   GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel);
-  uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel);
-  if (!(linkTypes & nsStyleLinkElement::eSTYLESHEET)) {
+  uint32_t linkTypes = ParseLinkTypes(rel);
+  if (!(linkTypes & eSTYLESHEET)) {
     return Nothing();
   }
 
   if (!IsCSSMimeTypeAttribute(*this)) {
     return Nothing();
   }
 
   if (StaticPrefs::dom_link_disabled_attribute_enabled() && Disabled()) {
     return Nothing();
   }
 
   nsAutoString title;
   nsAutoString media;
   GetTitleAndMediaForElement(*this, title, media);
 
-  bool alternate = linkTypes & nsStyleLinkElement::eALTERNATE;
+  bool alternate = linkTypes & eALTERNATE;
   if (alternate && title.IsEmpty()) {
     // alternates must have title.
     return Nothing();
   }
 
   nsAutoString href;
   GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
   if (href.IsEmpty()) {
@@ -481,16 +484,279 @@ JSObject* HTMLLinkElement::WrapNode(JSCo
                                     JS::Handle<JSObject*> aGivenProto) {
   return HTMLLinkElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void HTMLLinkElement::GetAs(nsAString& aResult) {
   GetEnumAttr(nsGkAtoms::as, EmptyCString().get(), aResult);
 }
 
+static const nsAttrValue::EnumTable kAsAttributeTable[] = {
+    {"", DESTINATION_INVALID},      {"audio", DESTINATION_AUDIO},
+    {"font", DESTINATION_FONT},     {"image", DESTINATION_IMAGE},
+    {"script", DESTINATION_SCRIPT}, {"style", DESTINATION_STYLE},
+    {"track", DESTINATION_TRACK},   {"video", DESTINATION_VIDEO},
+    {"fetch", DESTINATION_FETCH},   {nullptr, 0}};
+
+/* static */
+void HTMLLinkElement::ParseAsValue(const nsAString& aValue,
+                                   nsAttrValue& aResult) {
+  DebugOnly<bool> success =
+      aResult.ParseEnumValue(aValue, kAsAttributeTable, false,
+                             // default value is a empty string
+                             // if aValue is not a value we
+                             // understand
+                             &kAsAttributeTable[0]);
+  MOZ_ASSERT(success);
+}
+
+/* static */
+nsContentPolicyType HTMLLinkElement::AsValueToContentPolicy(
+    const nsAttrValue& aValue) {
+  switch (aValue.GetEnumValue()) {
+    case DESTINATION_INVALID:
+      return nsIContentPolicy::TYPE_INVALID;
+    case DESTINATION_AUDIO:
+      return nsIContentPolicy::TYPE_INTERNAL_AUDIO;
+    case DESTINATION_TRACK:
+      return nsIContentPolicy::TYPE_INTERNAL_TRACK;
+    case DESTINATION_VIDEO:
+      return nsIContentPolicy::TYPE_INTERNAL_VIDEO;
+    case DESTINATION_FONT:
+      return nsIContentPolicy::TYPE_FONT;
+    case DESTINATION_IMAGE:
+      return nsIContentPolicy::TYPE_IMAGE;
+    case DESTINATION_SCRIPT:
+      return nsIContentPolicy::TYPE_SCRIPT;
+    case DESTINATION_STYLE:
+      return nsIContentPolicy::TYPE_STYLESHEET;
+    case DESTINATION_FETCH:
+      return nsIContentPolicy::TYPE_OTHER;
+  }
+  return nsIContentPolicy::TYPE_INVALID;
+}
+
+void HTMLLinkElement::GetContentPolicyMimeTypeMedia(
+    nsAttrValue& aAsAttr, nsContentPolicyType& aPolicyType, nsString& aMimeType,
+    nsAString& aMedia) {
+  nsAutoString as;
+  GetAttr(kNameSpaceID_None, nsGkAtoms::as, as);
+  ParseAsValue(as, aAsAttr);
+  aPolicyType = AsValueToContentPolicy(aAsAttr);
+
+  nsAutoString type;
+  GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
+  nsAutoString notUsed;
+  nsContentUtils::SplitMimeType(type, aMimeType, notUsed);
+
+  GetAttr(kNameSpaceID_None, nsGkAtoms::media, aMedia);
+}
+
+void HTMLLinkElement::
+    TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender() {
+  MOZ_ASSERT(IsInComposedDoc());
+  if (!ElementHasHref()) {
+    return;
+  }
+
+  nsAutoString rel;
+  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
+    return;
+  }
+
+  if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
+    return;
+  }
+
+  uint32_t linkTypes = ParseLinkTypes(rel);
+
+  if ((linkTypes & ePREFETCH) || (linkTypes & eNEXT) ||
+      (linkTypes & ePRELOAD)) {
+    nsCOMPtr<nsIPrefetchService> prefetchService(
+        components::Prefetch::Service());
+    if (prefetchService) {
+      nsCOMPtr<nsIURI> uri(GetURI());
+      if (uri) {
+        bool preload = !!(linkTypes & ePRELOAD);
+        nsContentPolicyType policyType;
+
+        if (preload) {
+          nsAttrValue asAttr;
+          nsAutoString mimeType;
+          nsAutoString media;
+          GetContentPolicyMimeTypeMedia(asAttr, policyType, mimeType, media);
+
+          if (policyType == nsIContentPolicy::TYPE_INVALID) {
+            // Ignore preload with a wrong or empty as attribute.
+            return;
+          }
+
+          if (!CheckPreloadAttrs(asAttr, mimeType, media, OwnerDoc())) {
+            policyType = nsIContentPolicy::TYPE_INVALID;
+          }
+        }
+
+        nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
+        referrerInfo->InitWithNode(this);
+        if (preload) {
+          prefetchService->PreloadURI(uri, referrerInfo, this, policyType);
+        } else {
+          prefetchService->PrefetchURI(uri, referrerInfo, this,
+                                       linkTypes & ePREFETCH);
+        }
+        return;
+      }
+    }
+  }
+
+  if (linkTypes & ePRECONNECT) {
+    if (nsCOMPtr<nsIURI> uri = GetURI()) {
+      OwnerDoc()->MaybePreconnect(
+          uri, AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)));
+      return;
+    }
+  }
+
+  if (linkTypes & eDNS_PREFETCH) {
+    if (nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) {
+      nsHTMLDNSPrefetch::PrefetchLow(this);
+    }
+  }
+}
+
+void HTMLLinkElement::UpdatePreload(nsAtom* aName, const nsAttrValue* aValue,
+                                    const nsAttrValue* aOldValue) {
+  MOZ_ASSERT(IsInComposedDoc());
+
+  if (!ElementHasHref()) {
+    return;
+  }
+
+  nsAutoString rel;
+  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel)) {
+    return;
+  }
+
+  if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
+    return;
+  }
+
+  uint32_t linkTypes = ParseLinkTypes(rel);
+
+  if (!(linkTypes & ePRELOAD)) {
+    return;
+  }
+
+  nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service());
+  if (!prefetchService) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri(GetURI());
+  if (!uri) {
+    return;
+  }
+
+  nsAttrValue asAttr;
+  nsContentPolicyType asPolicyType;
+  nsAutoString mimeType;
+  nsAutoString media;
+  GetContentPolicyMimeTypeMedia(asAttr, asPolicyType, mimeType, media);
+
+  if (asPolicyType == nsIContentPolicy::TYPE_INVALID) {
+    // Ignore preload with a wrong or empty as attribute, but be sure to cancel
+    // the old one.
+    prefetchService->CancelPrefetchPreloadURI(uri, this);
+    return;
+  }
+
+  nsContentPolicyType policyType = asPolicyType;
+  if (!CheckPreloadAttrs(asAttr, mimeType, media, OwnerDoc())) {
+    policyType = nsIContentPolicy::TYPE_INVALID;
+  }
+
+  if (aName == nsGkAtoms::crossorigin) {
+    CORSMode corsMode = AttrValueToCORSMode(aValue);
+    CORSMode oldCorsMode = AttrValueToCORSMode(aOldValue);
+    if (corsMode != oldCorsMode) {
+      prefetchService->CancelPrefetchPreloadURI(uri, this);
+
+      nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
+      referrerInfo->InitWithNode(this);
+      prefetchService->PreloadURI(uri, referrerInfo, this, policyType);
+    }
+    return;
+  }
+
+  nsContentPolicyType oldPolicyType;
+
+  if (aName == nsGkAtoms::as) {
+    if (aOldValue) {
+      oldPolicyType = AsValueToContentPolicy(*aOldValue);
+      if (!CheckPreloadAttrs(*aOldValue, mimeType, media, OwnerDoc())) {
+        oldPolicyType = nsIContentPolicy::TYPE_INVALID;
+      }
+    } else {
+      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
+    }
+  } else if (aName == nsGkAtoms::type) {
+    nsAutoString oldType;
+    nsAutoString notUsed;
+    if (aOldValue) {
+      aOldValue->ToString(oldType);
+    } else {
+      oldType = EmptyString();
+    }
+    nsAutoString oldMimeType;
+    nsContentUtils::SplitMimeType(oldType, oldMimeType, notUsed);
+    if (CheckPreloadAttrs(asAttr, oldMimeType, media, OwnerDoc())) {
+      oldPolicyType = asPolicyType;
+    } else {
+      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
+    }
+  } else {
+    MOZ_ASSERT(aName == nsGkAtoms::media);
+    nsAutoString oldMedia;
+    if (aOldValue) {
+      aOldValue->ToString(oldMedia);
+    } else {
+      oldMedia = EmptyString();
+    }
+    if (CheckPreloadAttrs(asAttr, mimeType, oldMedia, OwnerDoc())) {
+      oldPolicyType = asPolicyType;
+    } else {
+      oldPolicyType = nsIContentPolicy::TYPE_INVALID;
+    }
+  }
+
+  if ((policyType != oldPolicyType) &&
+      (oldPolicyType != nsIContentPolicy::TYPE_INVALID)) {
+    prefetchService->CancelPrefetchPreloadURI(uri, this);
+  }
+
+  // Trigger a new preload if the policy type has changed.
+  // Also trigger load if the new policy type is invalid, this will only
+  // trigger an error event.
+  if ((policyType != oldPolicyType) ||
+      (policyType == nsIContentPolicy::TYPE_INVALID)) {
+    nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
+    referrerInfo->InitWithNode(this);
+    prefetchService->PreloadURI(uri, referrerInfo, this, policyType);
+  }
+}
+
+void HTMLLinkElement::CancelPrefetchOrPreload() {
+  nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service());
+  if (prefetchService) {
+    if (nsCOMPtr<nsIURI> uri = GetURI()) {
+      prefetchService->CancelPrefetchPreloadURI(uri, this);
+    }
+  }
+}
+
 // We will use official mime-types from:
 // https://www.iana.org/assignments/media-types/media-types.xhtml#font
 // We do not support old deprecated mime-types for preload feature.
 // (We currectly do not support font/collection)
 static uint32_t StyleLinkElementFontMimeTypesNum = 5;
 static const char* StyleLinkElementFontMimeTypes[] = {
     "font/otf", "font/sfnt", "font/ttf", "font/woff", "font/woff2"};
 
@@ -505,17 +771,17 @@ bool IsFontMimeType(const nsAString& aTy
   }
   return false;
 }
 
 bool HTMLLinkElement::CheckPreloadAttrs(const nsAttrValue& aAs,
                                         const nsAString& aType,
                                         const nsAString& aMedia,
                                         Document* aDocument) {
-  nsContentPolicyType policyType = Link::AsValueToContentPolicy(aAs);
+  nsContentPolicyType policyType = AsValueToContentPolicy(aAs);
   if (policyType == nsIContentPolicy::TYPE_INVALID) {
     return false;
   }
 
   // Check if media attribute is valid.
   if (!aMedia.IsEmpty()) {
     RefPtr<MediaList> mediaList = MediaList::Create(aMedia);
     if (!mediaList->Matches(*aDocument)) {
--- a/dom/html/HTMLLinkElement.h
+++ b/dom/html/HTMLLinkElement.h
@@ -115,16 +115,20 @@ class HTMLLinkElement final : public nsG
   }
   void SetHreflang(const nsAString& aHreflang, ErrorResult& aRv) {
     SetHTMLAttr(nsGkAtoms::hreflang, aHreflang, aRv);
   }
   void GetAs(nsAString& aResult);
   void SetAs(const nsAString& aAs, ErrorResult& aRv) {
     SetAttr(nsGkAtoms::as, aAs, aRv);
   }
+
+  static void ParseAsValue(const nsAString& aValue, nsAttrValue& aResult);
+  static nsContentPolicyType AsValueToContentPolicy(const nsAttrValue& aValue);
+
   nsDOMTokenList* Sizes() { return GetTokenList(nsGkAtoms::sizes); }
   void GetType(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::type, aValue); }
   void SetType(const nsAString& aType, ErrorResult& aRv) {
     SetHTMLAttr(nsGkAtoms::type, aType, aRv);
   }
   void GetCharset(nsAString& aValue) override {
     GetHTMLAttr(nsGkAtoms::charset, aValue);
   }
@@ -162,16 +166,24 @@ class HTMLLinkElement final : public nsG
   }
 
   static bool CheckPreloadAttrs(const nsAttrValue& aAs, const nsAString& aType,
                                 const nsAString& aMedia, Document* aDocument);
 
  protected:
   virtual ~HTMLLinkElement();
 
+  void GetContentPolicyMimeTypeMedia(nsAttrValue& aAsAttr,
+                                     nsContentPolicyType& aPolicyType,
+                                     nsString& aMimeType, nsAString& aMedia);
+  void TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
+  void UpdatePreload(nsAtom* aName, const nsAttrValue* aValue,
+                     const nsAttrValue* aOldValue);
+  void CancelPrefetchOrPreload();
+
   // nsStyleLinkElement
   Maybe<SheetInfo> GetStyleSheetInfo() final;
 
   RefPtr<nsDOMTokenList> mRelList;
 
   // The "explicitly enabled" flag. This flag is set whenever the `disabled`
   // attribute is explicitly unset, and makes alternate stylesheets not be
   // disabled by default anymore.