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 "mozilla/dom/HTMLLinkElement.h"
9 #include "mozilla/AsyncEventDispatcher.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Components.h"
12 #include "mozilla/EventDispatcher.h"
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/StaticPrefs_dom.h"
16 #include "mozilla/StaticPrefs_network.h"
17 #include "mozilla/dom/BindContext.h"
18 #include "mozilla/dom/DocumentInlines.h"
19 #include "mozilla/dom/HTMLLinkElementBinding.h"
20 #include "mozilla/dom/HTMLDNSPrefetch.h"
21 #include "mozilla/dom/ReferrerInfo.h"
22 #include "mozilla/dom/ScriptLoader.h"
23 #include "nsContentUtils.h"
24 #include "nsDOMTokenList.h"
25 #include "nsGenericHTMLElement.h"
26 #include "nsGkAtoms.h"
27 #include "nsIContentInlines.h"
28 #include "mozilla/dom/Document.h"
30 #include "nsIPrefetchService.h"
31 #include "nsISizeOf.h"
32 #include "nsPIDOMWindow.h"
33 #include "nsReadableUtils.h"
34 #include "nsStyleConsts.h"
35 #include "nsUnicharUtils.h"
36 #include "nsWindowSizes.h"
37 #include "nsIContentPolicy.h"
38 #include "nsMimeTypes.h"
39 #include "imgLoader.h"
40 #include "MediaContainerType.h"
41 #include "DecoderDoctorDiagnostics.h"
42 #include "DecoderTraits.h"
43 #include "MediaList.h"
44 #include "nsAttrValueInlines.h"
46 NS_IMPL_NS_NEW_HTML_ELEMENT(Link
)
48 namespace mozilla::dom
{
50 HTMLLinkElement::HTMLLinkElement(
51 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
52 : nsGenericHTMLElement(std::move(aNodeInfo
)) {}
54 HTMLLinkElement::~HTMLLinkElement() { SupportsDNSPrefetch::Destroyed(*this); }
56 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLLinkElement
)
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLLinkElement
,
60 tmp
->LinkStyle::Traverse(cb
);
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList
)
62 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSizes
)
63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
65 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLLinkElement
,
67 tmp
->LinkStyle::Unlink();
68 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList
)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSizes
)
70 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
72 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLLinkElement
,
75 NS_IMPL_ELEMENT_CLONE(HTMLLinkElement
)
77 bool HTMLLinkElement::Disabled() const {
78 return GetBoolAttr(nsGkAtoms::disabled
);
81 void HTMLLinkElement::SetDisabled(bool aDisabled
, ErrorResult
& aRv
) {
82 return SetHTMLBoolAttr(nsGkAtoms::disabled
, aDisabled
, aRv
);
85 nsresult
HTMLLinkElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
86 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
87 NS_ENSURE_SUCCESS(rv
, rv
);
89 if (IsInComposedDoc()) {
90 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
93 LinkStyle::BindToTree();
95 if (IsInUncomposedDoc() &&
96 AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
98 aContext
.OwnerDoc().LocalizationLinkAdded(this);
106 void HTMLLinkElement::LinkAdded() {
107 CreateAndDispatchEvent(OwnerDoc(), u
"DOMLinkAdded"_ns
);
110 void HTMLLinkElement::LinkRemoved() {
111 CreateAndDispatchEvent(OwnerDoc(), u
"DOMLinkRemoved"_ns
);
114 void HTMLLinkElement::UnbindFromTree(bool aNullParent
) {
115 CancelDNSPrefetch(*this);
116 CancelPrefetchOrPreload();
118 // If this is reinserted back into the document it will not be
120 Document
* oldDoc
= GetUncomposedDoc();
121 ShadowRoot
* oldShadowRoot
= GetContainingShadow();
123 // We want to update the localization but only if the link is removed from a
124 // DOM change, and not because the document is going away.
126 if (oldDoc
&& oldDoc
->GetScriptHandlingObject(ignore
) &&
127 AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
129 oldDoc
->LocalizationLinkRemoved(this);
132 CreateAndDispatchEvent(oldDoc
, u
"DOMLinkRemoved"_ns
);
133 nsGenericHTMLElement::UnbindFromTree(aNullParent
);
135 Unused
<< UpdateStyleSheetInternal(oldDoc
, oldShadowRoot
);
138 bool HTMLLinkElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
139 const nsAString
& aValue
,
140 nsIPrincipal
* aMaybeScriptedPrincipal
,
141 nsAttrValue
& aResult
) {
142 if (aNamespaceID
== kNameSpaceID_None
) {
143 if (aAttribute
== nsGkAtoms::crossorigin
) {
144 ParseCORSValue(aValue
, aResult
);
148 if (aAttribute
== nsGkAtoms::as
) {
149 net::ParseAsValue(aValue
, aResult
);
153 if (aAttribute
== nsGkAtoms::sizes
) {
154 aResult
.ParseAtomArray(aValue
);
158 if (aAttribute
== nsGkAtoms::integrity
) {
159 aResult
.ParseStringOrAtom(aValue
);
164 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
165 aMaybeScriptedPrincipal
, aResult
);
168 void HTMLLinkElement::CreateAndDispatchEvent(Document
* aDoc
,
169 const nsAString
& aEventName
) {
172 // In the unlikely case that both rev is specified *and* rel=stylesheet,
173 // this code will cause the event to fire, on the principle that maybe the
174 // page really does want to specify that its author is a stylesheet. Since
175 // this should never actually happen and the performance hit is minimal,
176 // doing the "right" thing costs virtually nothing here, even if it doesn't
178 static AttrArray::AttrValuesArray strings
[] = {
179 nsGkAtoms::_empty
, nsGkAtoms::stylesheet
, nullptr};
181 if (!nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None
,
183 FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::rel
, strings
,
184 eIgnoreCase
) != AttrArray::ATTR_VALUE_NO_MATCH
) {
188 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
= new AsyncEventDispatcher(
189 this, aEventName
, CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
190 // Always run async in order to avoid running script when the content
191 // sink isn't expecting it.
192 asyncDispatcher
->PostDOMEvent();
195 void HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
196 const nsAttrValue
* aValue
, bool aNotify
) {
197 if (aNameSpaceID
== kNameSpaceID_None
&&
198 (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
)) {
199 CancelDNSPrefetch(*this);
200 CancelPrefetchOrPreload();
203 return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID
, aName
, aValue
,
207 void HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
208 const nsAttrValue
* aValue
,
209 const nsAttrValue
* aOldValue
,
210 nsIPrincipal
* aSubjectPrincipal
,
212 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::href
) {
213 mCachedURI
= nullptr;
214 if (IsInUncomposedDoc()) {
215 CreateAndDispatchEvent(OwnerDoc(), u
"DOMLinkChanged"_ns
);
217 mTriggeringPrincipal
= nsContentUtils::GetAttrTriggeringPrincipal(
218 this, aValue
? aValue
->GetStringValue() : EmptyString(),
221 // If the link has `rel=localization` and its `href` attribute is changed,
222 // update the list of localization links.
223 if (AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
225 if (Document
* doc
= GetUncomposedDoc()) {
227 doc
->LocalizationLinkRemoved(this);
230 doc
->LocalizationLinkAdded(this);
236 // If a link's `rel` attribute was changed from or to `localization`,
237 // update the list of localization links.
238 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::rel
) {
239 if (Document
* doc
= GetUncomposedDoc()) {
240 if ((aValue
&& aValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
)) &&
242 !aOldValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
))) {
243 doc
->LocalizationLinkAdded(this);
244 } else if ((aOldValue
&&
245 aOldValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
)) &&
247 !aValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
))) {
248 doc
->LocalizationLinkRemoved(this);
254 if (aNameSpaceID
== kNameSpaceID_None
&&
255 (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
||
256 aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
257 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::as
||
258 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::disabled
)) {
259 bool dropSheet
= false;
260 if (aName
== nsGkAtoms::rel
) {
262 aValue
->ToString(value
);
263 uint32_t linkTypes
= ParseLinkTypes(value
);
265 dropSheet
= !(linkTypes
& eSTYLESHEET
);
269 if ((aName
== nsGkAtoms::rel
|| aName
== nsGkAtoms::href
) &&
271 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
274 if ((aName
== nsGkAtoms::as
|| aName
== nsGkAtoms::type
||
275 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::media
) &&
277 UpdatePreload(aName
, aValue
, aOldValue
);
280 const bool forceUpdate
=
281 dropSheet
|| aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
282 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::disabled
;
284 Unused
<< UpdateStyleSheetInternal(
285 nullptr, nullptr, forceUpdate
? ForceUpdate::Yes
: ForceUpdate::No
);
288 if (aNameSpaceID
== kNameSpaceID_None
) {
289 if (aName
== nsGkAtoms::disabled
) {
290 mExplicitlyEnabled
= true;
292 // Since removing href or rel makes us no longer link to a stylesheet,
293 // force updates for those too.
294 if (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
||
295 aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
296 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::disabled
) {
297 Unused
<< UpdateStyleSheetInternal(nullptr, nullptr, ForceUpdate::Yes
);
299 if ((aName
== nsGkAtoms::as
|| aName
== nsGkAtoms::type
||
300 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::media
) &&
302 UpdatePreload(aName
, aValue
, aOldValue
);
307 return nsGenericHTMLElement::AfterSetAttr(
308 aNameSpaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
311 // Keep this and the arrays below in sync with ToLinkMask in LinkStyle.cpp.
312 #define SUPPORTED_REL_VALUES_BASE \
313 "preload", "prefetch", "dns-prefetch", "stylesheet", "next", "alternate", \
314 "preconnect", "icon", "search", nullptr
316 static const DOMTokenListSupportedToken sSupportedRelValueCombinations
[][12] = {
317 {SUPPORTED_REL_VALUES_BASE
},
318 {"manifest", SUPPORTED_REL_VALUES_BASE
},
319 {"modulepreload", SUPPORTED_REL_VALUES_BASE
},
320 {"modulepreload", "manifest", SUPPORTED_REL_VALUES_BASE
}};
321 #undef SUPPORTED_REL_VALUES_BASE
323 nsDOMTokenList
* HTMLLinkElement::RelList() {
325 int index
= (StaticPrefs::dom_manifest_enabled() ? 1 : 0) |
326 (StaticPrefs::network_modulepreload() ? 2 : 0);
328 mRelList
= new nsDOMTokenList(this, nsGkAtoms::rel
,
329 sSupportedRelValueCombinations
[index
]);
334 Maybe
<LinkStyle::SheetInfo
> HTMLLinkElement::GetStyleSheetInfo() {
336 GetAttr(nsGkAtoms::rel
, rel
);
337 uint32_t linkTypes
= ParseLinkTypes(rel
);
338 if (!(linkTypes
& eSTYLESHEET
)) {
342 if (!IsCSSMimeTypeAttributeForLinkElement(*this)) {
352 GetTitleAndMediaForElement(*this, title
, media
);
354 bool alternate
= linkTypes
& eALTERNATE
;
355 if (alternate
&& title
.IsEmpty()) {
356 // alternates must have title.
360 if (!HasNonEmptyAttr(nsGkAtoms::href
)) {
364 nsAutoString integrity
;
365 GetAttr(nsGkAtoms::integrity
, integrity
);
367 nsCOMPtr
<nsIURI
> uri
= GetURI();
368 nsCOMPtr
<nsIPrincipal
> prin
= mTriggeringPrincipal
;
371 nsString
* cspNonce
= static_cast<nsString
*>(GetProperty(nsGkAtoms::nonce
));
376 return Some(SheetInfo
{
381 MakeAndAddRef
<ReferrerInfo
>(*this),
387 alternate
? HasAlternateRel::Yes
: HasAlternateRel::No
,
389 mExplicitlyEnabled
? IsExplicitlyEnabled::Yes
: IsExplicitlyEnabled::No
,
393 void HTMLLinkElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
394 size_t* aNodeSize
) const {
395 nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
396 if (nsCOMPtr
<nsISizeOf
> iface
= do_QueryInterface(mCachedURI
)) {
397 *aNodeSize
+= iface
->SizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
401 JSObject
* HTMLLinkElement::WrapNode(JSContext
* aCx
,
402 JS::Handle
<JSObject
*> aGivenProto
) {
403 return HTMLLinkElement_Binding::Wrap(aCx
, this, aGivenProto
);
406 void HTMLLinkElement::GetAs(nsAString
& aResult
) {
407 GetEnumAttr(nsGkAtoms::as
, "", aResult
);
410 void HTMLLinkElement::GetContentPolicyMimeTypeMedia(
411 nsAttrValue
& aAsAttr
, nsContentPolicyType
& aPolicyType
, nsString
& aMimeType
,
414 GetAttr(nsGkAtoms::as
, as
);
415 net::ParseAsValue(as
, aAsAttr
);
416 aPolicyType
= net::AsValueToContentPolicy(aAsAttr
);
419 GetAttr(nsGkAtoms::type
, type
);
420 nsAutoString notUsed
;
421 nsContentUtils::SplitMimeType(type
, aMimeType
, notUsed
);
423 GetAttr(nsGkAtoms::media
, aMedia
);
426 void HTMLLinkElement::
427 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender() {
428 MOZ_ASSERT(IsInComposedDoc());
429 if (!HasAttr(nsGkAtoms::href
)) {
434 if (!GetAttr(nsGkAtoms::rel
, rel
)) {
438 if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
442 uint32_t linkTypes
= ParseLinkTypes(rel
);
444 if ((linkTypes
& ePREFETCH
) || (linkTypes
& eNEXT
)) {
445 nsCOMPtr
<nsIPrefetchService
> prefetchService(
446 components::Prefetch::Service());
447 if (prefetchService
) {
448 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
449 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*this);
450 prefetchService
->PrefetchURI(uri
, referrerInfo
, this,
451 linkTypes
& ePREFETCH
);
457 if (linkTypes
& ePRELOAD
) {
458 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
459 nsContentPolicyType policyType
;
462 nsAutoString mimeType
;
464 GetContentPolicyMimeTypeMedia(asAttr
, policyType
, mimeType
, media
);
466 if (policyType
== nsIContentPolicy::TYPE_INVALID
||
467 !net::CheckPreloadAttrs(asAttr
, mimeType
, media
, OwnerDoc())) {
468 // Ignore preload with a wrong or empty as attribute.
469 net::WarnIgnoredPreload(*OwnerDoc(), *uri
);
473 StartPreload(policyType
);
478 if (linkTypes
& eMODULE_PRELOAD
) {
479 ScriptLoader
* scriptLoader
= OwnerDoc()->ScriptLoader();
480 ModuleLoader
* moduleLoader
= scriptLoader
->GetModuleLoader();
483 // For the print preview documents, at this moment it doesn't have module
484 // loader yet, as the (print preview) document is not attached to the
485 // nsIContentViewer yet, so it doesn't have the GlobalObject.
486 // Also, the script elements won't be processed as they are also cloned
487 // from the original document.
488 // So we simply bail out if the module loader is null.
492 if (!StaticPrefs::network_modulepreload()) {
493 // Keep behavior from https://phabricator.services.mozilla.com/D149371,
494 // prior to main implementation of modulepreload
495 moduleLoader
->DisallowImportMaps();
499 // https://html.spec.whatwg.org/multipage/semantics.html#processing-the-media-attribute
500 // TODO: apply this check for all linkTypes
502 if (GetAttr(nsGkAtoms::media
, media
)) {
503 RefPtr
<mozilla::dom::MediaList
> mediaList
=
504 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(media
));
505 if (!mediaList
->Matches(*OwnerDoc())) {
510 // TODO: per spec, apply this check for ePREFETCH as well
511 if (!HasNonEmptyAttr(nsGkAtoms::href
)) {
516 GetAttr(nsGkAtoms::as
, as
);
518 if (!net::IsScriptLikeOrInvalid(as
)) {
519 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
= new AsyncEventDispatcher(
520 this, u
"error"_ns
, CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
521 asyncDispatcher
->PostDOMEvent();
525 nsCOMPtr
<nsIURI
> uri
= GetURI();
530 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-modulepreload-module-script-graph
531 // Step 1. Disallow further import maps given settings object.
532 moduleLoader
->DisallowImportMaps();
534 StartPreload(nsIContentPolicy::TYPE_SCRIPT
);
538 if (linkTypes
& ePRECONNECT
) {
539 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
540 OwnerDoc()->MaybePreconnect(
541 uri
, AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin
)));
546 if (linkTypes
& eDNS_PREFETCH
) {
547 TryDNSPrefetch(*this);
551 void HTMLLinkElement::UpdatePreload(nsAtom
* aName
, const nsAttrValue
* aValue
,
552 const nsAttrValue
* aOldValue
) {
553 MOZ_ASSERT(IsInComposedDoc());
555 if (!HasAttr(nsGkAtoms::href
)) {
560 if (!GetAttr(nsGkAtoms::rel
, rel
)) {
564 if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
568 uint32_t linkTypes
= ParseLinkTypes(rel
);
570 if (!(linkTypes
& ePRELOAD
)) {
574 nsCOMPtr
<nsIURI
> uri
= GetURI();
580 nsContentPolicyType asPolicyType
;
581 nsAutoString mimeType
;
583 GetContentPolicyMimeTypeMedia(asAttr
, asPolicyType
, mimeType
, media
);
585 if (asPolicyType
== nsIContentPolicy::TYPE_INVALID
||
586 !net::CheckPreloadAttrs(asAttr
, mimeType
, media
, OwnerDoc())) {
587 // Ignore preload with a wrong or empty as attribute, but be sure to cancel
589 CancelPrefetchOrPreload();
590 net::WarnIgnoredPreload(*OwnerDoc(), *uri
);
594 if (aName
== nsGkAtoms::crossorigin
) {
595 CORSMode corsMode
= AttrValueToCORSMode(aValue
);
596 CORSMode oldCorsMode
= AttrValueToCORSMode(aOldValue
);
597 if (corsMode
!= oldCorsMode
) {
598 CancelPrefetchOrPreload();
599 StartPreload(asPolicyType
);
604 nsContentPolicyType oldPolicyType
;
606 if (aName
== nsGkAtoms::as
) {
608 oldPolicyType
= net::AsValueToContentPolicy(*aOldValue
);
609 if (!net::CheckPreloadAttrs(*aOldValue
, mimeType
, media
, OwnerDoc())) {
610 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
613 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
615 } else if (aName
== nsGkAtoms::type
) {
616 nsAutoString oldType
;
617 nsAutoString notUsed
;
619 aOldValue
->ToString(oldType
);
621 nsAutoString oldMimeType
;
622 nsContentUtils::SplitMimeType(oldType
, oldMimeType
, notUsed
);
623 if (net::CheckPreloadAttrs(asAttr
, oldMimeType
, media
, OwnerDoc())) {
624 oldPolicyType
= asPolicyType
;
626 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
629 MOZ_ASSERT(aName
== nsGkAtoms::media
);
630 nsAutoString oldMedia
;
632 aOldValue
->ToString(oldMedia
);
634 if (net::CheckPreloadAttrs(asAttr
, mimeType
, oldMedia
, OwnerDoc())) {
635 oldPolicyType
= asPolicyType
;
637 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
641 if (asPolicyType
!= oldPolicyType
&&
642 oldPolicyType
!= nsIContentPolicy::TYPE_INVALID
) {
643 CancelPrefetchOrPreload();
646 // Trigger a new preload if the policy type has changed.
647 if (asPolicyType
!= oldPolicyType
) {
648 StartPreload(asPolicyType
);
652 void HTMLLinkElement::CancelPrefetchOrPreload() {
655 nsCOMPtr
<nsIPrefetchService
> prefetchService(components::Prefetch::Service());
656 if (prefetchService
) {
657 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
658 prefetchService
->CancelPrefetchPreloadURI(uri
, this);
663 void HTMLLinkElement::StartPreload(nsContentPolicyType aPolicyType
) {
664 MOZ_ASSERT(!mPreload
, "Forgot to cancel the running preload");
665 RefPtr
<PreloaderBase
> preload
=
666 OwnerDoc()->Preloads().PreloadLinkElement(this, aPolicyType
);
667 mPreload
= preload
.get();
670 void HTMLLinkElement::CancelPreload() {
672 // This will cancel the loading channel if this was the last referred node
673 // and the preload is not used up until now to satisfy a regular tag load
675 mPreload
->RemoveLinkPreloadNode(this);
680 bool HTMLLinkElement::IsCSSMimeTypeAttributeForLinkElement(
681 const Element
& aSelf
) {
682 // Processing the type attribute per
683 // https://html.spec.whatwg.org/multipage/semantics.html#processing-the-type-attribute
684 // for HTML link elements.
686 nsAutoString mimeType
;
687 nsAutoString notUsed
;
688 aSelf
.GetAttr(nsGkAtoms::type
, type
);
689 nsContentUtils::SplitMimeType(type
, mimeType
, notUsed
);
690 return mimeType
.IsEmpty() || mimeType
.LowerCaseEqualsLiteral("text/css");
693 } // namespace mozilla::dom