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 if (AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
98 aContext
.OwnerDoc().LocalizationLinkAdded(this);
107 void HTMLLinkElement::LinkAdded() {
108 CreateAndDispatchEvent(u
"DOMLinkAdded"_ns
);
111 void HTMLLinkElement::UnbindFromTree(bool aNullParent
) {
112 CancelDNSPrefetch(*this);
113 CancelPrefetchOrPreload();
115 // If this is reinserted back into the document it will not be
117 Document
* oldDoc
= GetUncomposedDoc();
118 ShadowRoot
* oldShadowRoot
= GetContainingShadow();
120 // We want to update the localization but only if the link is removed from a
121 // DOM change, and not because the document is going away.
124 if (oldDoc
->GetScriptHandlingObject(ignore
) &&
125 AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
127 oldDoc
->LocalizationLinkRemoved(this);
131 nsGenericHTMLElement::UnbindFromTree(aNullParent
);
133 Unused
<< UpdateStyleSheetInternal(oldDoc
, oldShadowRoot
);
136 bool HTMLLinkElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
137 const nsAString
& aValue
,
138 nsIPrincipal
* aMaybeScriptedPrincipal
,
139 nsAttrValue
& aResult
) {
140 if (aNamespaceID
== kNameSpaceID_None
) {
141 if (aAttribute
== nsGkAtoms::crossorigin
) {
142 ParseCORSValue(aValue
, aResult
);
146 if (aAttribute
== nsGkAtoms::as
) {
147 net::ParseAsValue(aValue
, aResult
);
151 if (aAttribute
== nsGkAtoms::sizes
) {
152 aResult
.ParseAtomArray(aValue
);
156 if (aAttribute
== nsGkAtoms::integrity
) {
157 aResult
.ParseStringOrAtom(aValue
);
161 if (aAttribute
== nsGkAtoms::fetchpriority
) {
162 ParseFetchPriority(aValue
, aResult
);
167 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
168 aMaybeScriptedPrincipal
, aResult
);
171 void HTMLLinkElement::CreateAndDispatchEvent(const nsAString
& aEventName
) {
172 MOZ_ASSERT(IsInUncomposedDoc());
174 // In the unlikely case that both rev is specified *and* rel=stylesheet,
175 // this code will cause the event to fire, on the principle that maybe the
176 // page really does want to specify that its author is a stylesheet. Since
177 // this should never actually happen and the performance hit is minimal,
178 // doing the "right" thing costs virtually nothing here, even if it doesn't
180 static AttrArray::AttrValuesArray strings
[] = {
181 nsGkAtoms::_empty
, nsGkAtoms::stylesheet
, nullptr};
183 if (!nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None
,
185 FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::rel
, strings
,
186 eIgnoreCase
) != AttrArray::ATTR_VALUE_NO_MATCH
) {
190 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
= new AsyncEventDispatcher(
191 this, aEventName
, CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
192 // Always run async in order to avoid running script when the content
193 // sink isn't expecting it.
194 asyncDispatcher
->PostDOMEvent();
197 void HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
198 const nsAttrValue
* aValue
, bool aNotify
) {
199 if (aNameSpaceID
== kNameSpaceID_None
&&
200 (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
)) {
201 CancelDNSPrefetch(*this);
202 CancelPrefetchOrPreload();
205 return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID
, aName
, aValue
,
209 void HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
210 const nsAttrValue
* aValue
,
211 const nsAttrValue
* aOldValue
,
212 nsIPrincipal
* aSubjectPrincipal
,
214 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::href
) {
215 mCachedURI
= nullptr;
216 if (IsInUncomposedDoc()) {
217 CreateAndDispatchEvent(u
"DOMLinkChanged"_ns
);
219 mTriggeringPrincipal
= nsContentUtils::GetAttrTriggeringPrincipal(
220 this, aValue
? aValue
->GetStringValue() : EmptyString(),
223 // If the link has `rel=localization` and its `href` attribute is changed,
224 // update the list of localization links.
225 if (AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
227 if (Document
* doc
= GetUncomposedDoc()) {
229 doc
->LocalizationLinkRemoved(this);
232 doc
->LocalizationLinkAdded(this);
238 // If a link's `rel` attribute was changed from or to `localization`,
239 // update the list of localization links.
240 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::rel
) {
241 if (Document
* doc
= GetUncomposedDoc()) {
242 if ((aValue
&& aValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
)) &&
244 !aOldValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
))) {
245 doc
->LocalizationLinkAdded(this);
246 } else if ((aOldValue
&&
247 aOldValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
)) &&
249 !aValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
))) {
250 doc
->LocalizationLinkRemoved(this);
256 if (aNameSpaceID
== kNameSpaceID_None
&&
257 (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
||
258 aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
259 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::as
||
260 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::disabled
)) {
261 bool dropSheet
= false;
262 if (aName
== nsGkAtoms::rel
) {
264 aValue
->ToString(value
);
265 uint32_t linkTypes
= ParseLinkTypes(value
);
267 dropSheet
= !(linkTypes
& eSTYLESHEET
);
271 if ((aName
== nsGkAtoms::rel
|| aName
== nsGkAtoms::href
) &&
273 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
276 if ((aName
== nsGkAtoms::as
|| aName
== nsGkAtoms::type
||
277 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::media
) &&
279 UpdatePreload(aName
, aValue
, aOldValue
);
282 const bool forceUpdate
=
283 dropSheet
|| aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
284 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::disabled
;
286 Unused
<< UpdateStyleSheetInternal(
287 nullptr, nullptr, forceUpdate
? ForceUpdate::Yes
: ForceUpdate::No
);
290 if (aNameSpaceID
== kNameSpaceID_None
) {
291 if (aName
== nsGkAtoms::disabled
) {
292 mExplicitlyEnabled
= true;
294 // Since removing href or rel makes us no longer link to a stylesheet,
295 // force updates for those too.
296 if (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
||
297 aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
298 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::disabled
) {
299 Unused
<< UpdateStyleSheetInternal(nullptr, nullptr, ForceUpdate::Yes
);
301 if ((aName
== nsGkAtoms::as
|| aName
== nsGkAtoms::type
||
302 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::media
) &&
304 UpdatePreload(aName
, aValue
, aOldValue
);
309 return nsGenericHTMLElement::AfterSetAttr(
310 aNameSpaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
313 // Keep this and the arrays below in sync with ToLinkMask in LinkStyle.cpp.
314 #define SUPPORTED_REL_VALUES_BASE \
315 "preload", "prefetch", "dns-prefetch", "stylesheet", "next", "alternate", \
316 "preconnect", "icon", "search", nullptr
318 static const DOMTokenListSupportedToken sSupportedRelValueCombinations
[][12] = {
319 {SUPPORTED_REL_VALUES_BASE
},
320 {"manifest", SUPPORTED_REL_VALUES_BASE
},
321 {"modulepreload", SUPPORTED_REL_VALUES_BASE
},
322 {"modulepreload", "manifest", SUPPORTED_REL_VALUES_BASE
}};
323 #undef SUPPORTED_REL_VALUES_BASE
325 nsDOMTokenList
* HTMLLinkElement::RelList() {
327 int index
= (StaticPrefs::dom_manifest_enabled() ? 1 : 0) |
328 (StaticPrefs::network_modulepreload() ? 2 : 0);
330 mRelList
= new nsDOMTokenList(this, nsGkAtoms::rel
,
331 sSupportedRelValueCombinations
[index
]);
336 Maybe
<LinkStyle::SheetInfo
> HTMLLinkElement::GetStyleSheetInfo() {
338 GetAttr(nsGkAtoms::rel
, rel
);
339 uint32_t linkTypes
= ParseLinkTypes(rel
);
340 if (!(linkTypes
& eSTYLESHEET
)) {
344 if (!IsCSSMimeTypeAttributeForLinkElement(*this)) {
354 GetTitleAndMediaForElement(*this, title
, media
);
356 bool alternate
= linkTypes
& eALTERNATE
;
357 if (alternate
&& title
.IsEmpty()) {
358 // alternates must have title.
362 if (!HasNonEmptyAttr(nsGkAtoms::href
)) {
366 nsAutoString integrity
;
367 GetAttr(nsGkAtoms::integrity
, integrity
);
369 nsCOMPtr
<nsIURI
> uri
= GetURI();
370 nsCOMPtr
<nsIPrincipal
> prin
= mTriggeringPrincipal
;
373 nsString
* cspNonce
= static_cast<nsString
*>(GetProperty(nsGkAtoms::nonce
));
378 return Some(SheetInfo
{
383 MakeAndAddRef
<ReferrerInfo
>(*this),
389 alternate
? HasAlternateRel::Yes
: HasAlternateRel::No
,
391 mExplicitlyEnabled
? IsExplicitlyEnabled::Yes
: IsExplicitlyEnabled::No
,
396 void HTMLLinkElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
397 size_t* aNodeSize
) const {
398 nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
399 if (nsCOMPtr
<nsISizeOf
> iface
= do_QueryInterface(mCachedURI
)) {
400 *aNodeSize
+= iface
->SizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
404 JSObject
* HTMLLinkElement::WrapNode(JSContext
* aCx
,
405 JS::Handle
<JSObject
*> aGivenProto
) {
406 return HTMLLinkElement_Binding::Wrap(aCx
, this, aGivenProto
);
409 void HTMLLinkElement::GetAs(nsAString
& aResult
) {
410 GetEnumAttr(nsGkAtoms::as
, "", aResult
);
413 void HTMLLinkElement::GetContentPolicyMimeTypeMedia(
414 nsAttrValue
& aAsAttr
, nsContentPolicyType
& aPolicyType
, nsString
& aMimeType
,
417 GetAttr(nsGkAtoms::as
, as
);
418 net::ParseAsValue(as
, aAsAttr
);
419 aPolicyType
= net::AsValueToContentPolicy(aAsAttr
);
422 GetAttr(nsGkAtoms::type
, type
);
423 nsAutoString notUsed
;
424 nsContentUtils::SplitMimeType(type
, aMimeType
, notUsed
);
426 GetAttr(nsGkAtoms::media
, aMedia
);
429 void HTMLLinkElement::
430 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender() {
431 MOZ_ASSERT(IsInComposedDoc());
432 if (!HasAttr(nsGkAtoms::href
)) {
437 if (!GetAttr(nsGkAtoms::rel
, rel
)) {
441 if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
445 uint32_t linkTypes
= ParseLinkTypes(rel
);
447 if ((linkTypes
& ePREFETCH
) || (linkTypes
& eNEXT
)) {
448 nsCOMPtr
<nsIPrefetchService
> prefetchService(
449 components::Prefetch::Service());
450 if (prefetchService
) {
451 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
452 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*this);
453 prefetchService
->PrefetchURI(uri
, referrerInfo
, this,
454 linkTypes
& ePREFETCH
);
460 if (linkTypes
& ePRELOAD
) {
461 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
462 nsContentPolicyType policyType
;
465 nsAutoString mimeType
;
467 GetContentPolicyMimeTypeMedia(asAttr
, policyType
, mimeType
, media
);
469 if (policyType
== nsIContentPolicy::TYPE_INVALID
||
470 !net::CheckPreloadAttrs(asAttr
, mimeType
, media
, OwnerDoc())) {
471 // Ignore preload with a wrong or empty as attribute.
472 net::WarnIgnoredPreload(*OwnerDoc(), *uri
);
476 StartPreload(policyType
);
481 if (linkTypes
& eMODULE_PRELOAD
) {
482 ScriptLoader
* scriptLoader
= OwnerDoc()->ScriptLoader();
483 ModuleLoader
* moduleLoader
= scriptLoader
->GetModuleLoader();
486 // For the print preview documents, at this moment it doesn't have module
487 // loader yet, as the (print preview) document is not attached to the
488 // nsIDocumentViewer yet, so it doesn't have the GlobalObject.
489 // Also, the script elements won't be processed as they are also cloned
490 // from the original document.
491 // So we simply bail out if the module loader is null.
495 if (!StaticPrefs::network_modulepreload()) {
496 // Keep behavior from https://phabricator.services.mozilla.com/D149371,
497 // prior to main implementation of modulepreload
498 moduleLoader
->DisallowImportMaps();
502 // https://html.spec.whatwg.org/multipage/semantics.html#processing-the-media-attribute
503 // TODO: apply this check for all linkTypes
505 if (GetAttr(nsGkAtoms::media
, media
)) {
506 RefPtr
<mozilla::dom::MediaList
> mediaList
=
507 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(media
));
508 if (!mediaList
->Matches(*OwnerDoc())) {
513 // TODO: per spec, apply this check for ePREFETCH as well
514 if (!HasNonEmptyAttr(nsGkAtoms::href
)) {
519 GetAttr(nsGkAtoms::as
, as
);
521 if (!net::IsScriptLikeOrInvalid(as
)) {
522 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
= new AsyncEventDispatcher(
523 this, u
"error"_ns
, CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
524 asyncDispatcher
->PostDOMEvent();
528 nsCOMPtr
<nsIURI
> uri
= GetURI();
533 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-modulepreload-module-script-graph
534 // Step 1. Disallow further import maps given settings object.
535 moduleLoader
->DisallowImportMaps();
537 StartPreload(nsIContentPolicy::TYPE_SCRIPT
);
541 if (linkTypes
& ePRECONNECT
) {
542 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
543 OwnerDoc()->MaybePreconnect(
544 uri
, AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin
)));
549 if (linkTypes
& eDNS_PREFETCH
) {
550 TryDNSPrefetch(*this);
554 void HTMLLinkElement::UpdatePreload(nsAtom
* aName
, const nsAttrValue
* aValue
,
555 const nsAttrValue
* aOldValue
) {
556 MOZ_ASSERT(IsInComposedDoc());
558 if (!HasAttr(nsGkAtoms::href
)) {
563 if (!GetAttr(nsGkAtoms::rel
, rel
)) {
567 if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
571 uint32_t linkTypes
= ParseLinkTypes(rel
);
573 if (!(linkTypes
& ePRELOAD
)) {
577 nsCOMPtr
<nsIURI
> uri
= GetURI();
583 nsContentPolicyType asPolicyType
;
584 nsAutoString mimeType
;
586 GetContentPolicyMimeTypeMedia(asAttr
, asPolicyType
, mimeType
, media
);
588 if (asPolicyType
== nsIContentPolicy::TYPE_INVALID
||
589 !net::CheckPreloadAttrs(asAttr
, mimeType
, media
, OwnerDoc())) {
590 // Ignore preload with a wrong or empty as attribute, but be sure to cancel
592 CancelPrefetchOrPreload();
593 net::WarnIgnoredPreload(*OwnerDoc(), *uri
);
597 if (aName
== nsGkAtoms::crossorigin
) {
598 CORSMode corsMode
= AttrValueToCORSMode(aValue
);
599 CORSMode oldCorsMode
= AttrValueToCORSMode(aOldValue
);
600 if (corsMode
!= oldCorsMode
) {
601 CancelPrefetchOrPreload();
602 StartPreload(asPolicyType
);
607 nsContentPolicyType oldPolicyType
;
609 if (aName
== nsGkAtoms::as
) {
611 oldPolicyType
= net::AsValueToContentPolicy(*aOldValue
);
612 if (!net::CheckPreloadAttrs(*aOldValue
, mimeType
, media
, OwnerDoc())) {
613 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
616 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
618 } else if (aName
== nsGkAtoms::type
) {
619 nsAutoString oldType
;
620 nsAutoString notUsed
;
622 aOldValue
->ToString(oldType
);
624 nsAutoString oldMimeType
;
625 nsContentUtils::SplitMimeType(oldType
, oldMimeType
, notUsed
);
626 if (net::CheckPreloadAttrs(asAttr
, oldMimeType
, media
, OwnerDoc())) {
627 oldPolicyType
= asPolicyType
;
629 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
632 MOZ_ASSERT(aName
== nsGkAtoms::media
);
633 nsAutoString oldMedia
;
635 aOldValue
->ToString(oldMedia
);
637 if (net::CheckPreloadAttrs(asAttr
, mimeType
, oldMedia
, OwnerDoc())) {
638 oldPolicyType
= asPolicyType
;
640 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
644 if (asPolicyType
!= oldPolicyType
&&
645 oldPolicyType
!= nsIContentPolicy::TYPE_INVALID
) {
646 CancelPrefetchOrPreload();
649 // Trigger a new preload if the policy type has changed.
650 if (asPolicyType
!= oldPolicyType
) {
651 StartPreload(asPolicyType
);
655 void HTMLLinkElement::CancelPrefetchOrPreload() {
658 nsCOMPtr
<nsIPrefetchService
> prefetchService(components::Prefetch::Service());
659 if (prefetchService
) {
660 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
661 prefetchService
->CancelPrefetchPreloadURI(uri
, this);
666 void HTMLLinkElement::StartPreload(nsContentPolicyType aPolicyType
) {
667 MOZ_ASSERT(!mPreload
, "Forgot to cancel the running preload");
668 RefPtr
<PreloaderBase
> preload
=
669 OwnerDoc()->Preloads().PreloadLinkElement(this, aPolicyType
);
670 mPreload
= preload
.get();
673 void HTMLLinkElement::CancelPreload() {
675 // This will cancel the loading channel if this was the last referred node
676 // and the preload is not used up until now to satisfy a regular tag load
678 mPreload
->RemoveLinkPreloadNode(this);
683 bool HTMLLinkElement::IsCSSMimeTypeAttributeForLinkElement(
684 const Element
& aSelf
) {
685 // Processing the type attribute per
686 // https://html.spec.whatwg.org/multipage/semantics.html#processing-the-type-attribute
687 // for HTML link elements.
689 nsAutoString mimeType
;
690 nsAutoString notUsed
;
691 aSelf
.GetAttr(nsGkAtoms::type
, type
);
692 nsContentUtils::SplitMimeType(type
, mimeType
, notUsed
);
693 return mimeType
.IsEmpty() || mimeType
.LowerCaseEqualsLiteral("text/css");
696 } // namespace mozilla::dom