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(mBlocking
)
64 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
66 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLLinkElement
,
68 tmp
->LinkStyle::Unlink();
69 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList
)
70 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSizes
)
71 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlocking
)
72 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
74 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLLinkElement
,
77 NS_IMPL_ELEMENT_CLONE(HTMLLinkElement
)
79 bool HTMLLinkElement::Disabled() const {
80 return GetBoolAttr(nsGkAtoms::disabled
);
83 void HTMLLinkElement::SetDisabled(bool aDisabled
, ErrorResult
& aRv
) {
84 return SetHTMLBoolAttr(nsGkAtoms::disabled
, aDisabled
, aRv
);
87 nsresult
HTMLLinkElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
88 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
89 NS_ENSURE_SUCCESS(rv
, rv
);
91 if (IsInComposedDoc()) {
92 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
95 LinkStyle::BindToTree();
97 if (IsInUncomposedDoc()) {
98 if (AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
100 aContext
.OwnerDoc().LocalizationLinkAdded(this);
109 void HTMLLinkElement::LinkAdded() {
110 CreateAndDispatchEvent(u
"DOMLinkAdded"_ns
);
113 void HTMLLinkElement::UnbindFromTree(UnbindContext
& aContext
) {
114 CancelDNSPrefetch(*this);
115 CancelPrefetchOrPreload();
117 // If this is reinserted back into the document it will not be
119 Document
* oldDoc
= GetUncomposedDoc();
120 ShadowRoot
* oldShadowRoot
= GetContainingShadow();
122 // We want to update the localization but only if the link is removed from a
123 // DOM change, and not because the document is going away.
126 if (oldDoc
->GetScriptHandlingObject(ignore
) &&
127 AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
129 oldDoc
->LocalizationLinkRemoved(this);
133 nsGenericHTMLElement::UnbindFromTree(aContext
);
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
);
163 if (aAttribute
== nsGkAtoms::fetchpriority
) {
164 ParseFetchPriority(aValue
, aResult
);
168 if (aAttribute
== nsGkAtoms::blocking
&&
169 StaticPrefs::dom_element_blocking_enabled()) {
170 aResult
.ParseAtomArray(aValue
);
175 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
176 aMaybeScriptedPrincipal
, aResult
);
179 void HTMLLinkElement::CreateAndDispatchEvent(const nsAString
& aEventName
) {
180 MOZ_ASSERT(IsInUncomposedDoc());
182 // In the unlikely case that both rev is specified *and* rel=stylesheet,
183 // this code will cause the event to fire, on the principle that maybe the
184 // page really does want to specify that its author is a stylesheet. Since
185 // this should never actually happen and the performance hit is minimal,
186 // doing the "right" thing costs virtually nothing here, even if it doesn't
188 static AttrArray::AttrValuesArray strings
[] = {
189 nsGkAtoms::_empty
, nsGkAtoms::stylesheet
, nullptr};
191 if (!nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None
,
193 FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::rel
, strings
,
194 eIgnoreCase
) != AttrArray::ATTR_VALUE_NO_MATCH
) {
198 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
= new AsyncEventDispatcher(
199 this, aEventName
, CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
200 // Always run async in order to avoid running script when the content
201 // sink isn't expecting it.
202 asyncDispatcher
->PostDOMEvent();
205 void HTMLLinkElement::BeforeSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
206 const nsAttrValue
* aValue
, bool aNotify
) {
207 if (aNameSpaceID
== kNameSpaceID_None
&&
208 (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
)) {
209 CancelDNSPrefetch(*this);
210 CancelPrefetchOrPreload();
213 return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID
, aName
, aValue
,
217 void HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
218 const nsAttrValue
* aValue
,
219 const nsAttrValue
* aOldValue
,
220 nsIPrincipal
* aSubjectPrincipal
,
222 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::href
) {
223 mCachedURI
= nullptr;
224 if (IsInUncomposedDoc()) {
225 CreateAndDispatchEvent(u
"DOMLinkChanged"_ns
);
227 mTriggeringPrincipal
= nsContentUtils::GetAttrTriggeringPrincipal(
228 this, aValue
? aValue
->GetStringValue() : EmptyString(),
231 // If the link has `rel=localization` and its `href` attribute is changed,
232 // update the list of localization links.
233 if (AttrValueIs(kNameSpaceID_None
, nsGkAtoms::rel
, nsGkAtoms::localization
,
235 if (Document
* doc
= GetUncomposedDoc()) {
237 doc
->LocalizationLinkRemoved(this);
240 doc
->LocalizationLinkAdded(this);
246 // If a link's `rel` attribute was changed from or to `localization`,
247 // update the list of localization links.
248 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::rel
) {
249 if (Document
* doc
= GetUncomposedDoc()) {
250 if ((aValue
&& aValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
)) &&
252 !aOldValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
))) {
253 doc
->LocalizationLinkAdded(this);
254 } else if ((aOldValue
&&
255 aOldValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
)) &&
257 !aValue
->Equals(nsGkAtoms::localization
, eIgnoreCase
))) {
258 doc
->LocalizationLinkRemoved(this);
264 if (aNameSpaceID
== kNameSpaceID_None
&&
265 (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
||
266 aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
267 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::as
||
268 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::disabled
)) {
269 bool dropSheet
= false;
270 if (aName
== nsGkAtoms::rel
) {
272 aValue
->ToString(value
);
273 uint32_t linkTypes
= ParseLinkTypes(value
);
275 dropSheet
= !(linkTypes
& eSTYLESHEET
);
279 if ((aName
== nsGkAtoms::rel
|| aName
== nsGkAtoms::href
) &&
281 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
284 if ((aName
== nsGkAtoms::as
|| aName
== nsGkAtoms::type
||
285 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::media
) &&
287 UpdatePreload(aName
, aValue
, aOldValue
);
290 const bool forceUpdate
=
291 dropSheet
|| aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
292 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::disabled
;
294 Unused
<< UpdateStyleSheetInternal(
295 nullptr, nullptr, forceUpdate
? ForceUpdate::Yes
: ForceUpdate::No
);
298 if (aNameSpaceID
== kNameSpaceID_None
) {
299 if (aName
== nsGkAtoms::disabled
) {
300 mExplicitlyEnabled
= true;
302 // Since removing href or rel makes us no longer link to a stylesheet,
303 // force updates for those too.
304 if (aName
== nsGkAtoms::href
|| aName
== nsGkAtoms::rel
||
305 aName
== nsGkAtoms::title
|| aName
== nsGkAtoms::media
||
306 aName
== nsGkAtoms::type
|| aName
== nsGkAtoms::disabled
) {
307 Unused
<< UpdateStyleSheetInternal(nullptr, nullptr, ForceUpdate::Yes
);
309 if ((aName
== nsGkAtoms::as
|| aName
== nsGkAtoms::type
||
310 aName
== nsGkAtoms::crossorigin
|| aName
== nsGkAtoms::media
) &&
312 UpdatePreload(aName
, aValue
, aOldValue
);
317 return nsGenericHTMLElement::AfterSetAttr(
318 aNameSpaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
321 // Keep this and the arrays below in sync with ToLinkMask in LinkStyle.cpp.
322 #define SUPPORTED_REL_VALUES_BASE \
323 "preload", "prefetch", "dns-prefetch", "stylesheet", "next", "alternate", \
324 "preconnect", "icon", "search", nullptr
326 static const DOMTokenListSupportedToken sSupportedRelValueCombinations
[][12] = {
327 {SUPPORTED_REL_VALUES_BASE
},
328 {"manifest", SUPPORTED_REL_VALUES_BASE
},
329 {"modulepreload", SUPPORTED_REL_VALUES_BASE
},
330 {"modulepreload", "manifest", SUPPORTED_REL_VALUES_BASE
}};
331 #undef SUPPORTED_REL_VALUES_BASE
333 nsDOMTokenList
* HTMLLinkElement::RelList() {
335 int index
= (StaticPrefs::dom_manifest_enabled() ? 1 : 0) |
336 (StaticPrefs::network_modulepreload() ? 2 : 0);
338 mRelList
= new nsDOMTokenList(this, nsGkAtoms::rel
,
339 sSupportedRelValueCombinations
[index
]);
344 Maybe
<LinkStyle::SheetInfo
> HTMLLinkElement::GetStyleSheetInfo() {
346 GetAttr(nsGkAtoms::rel
, rel
);
347 uint32_t linkTypes
= ParseLinkTypes(rel
);
348 if (!(linkTypes
& eSTYLESHEET
)) {
352 if (!IsCSSMimeTypeAttributeForLinkElement(*this)) {
362 GetTitleAndMediaForElement(*this, title
, media
);
364 bool alternate
= linkTypes
& eALTERNATE
;
365 if (alternate
&& title
.IsEmpty()) {
366 // alternates must have title.
370 if (!HasNonEmptyAttr(nsGkAtoms::href
)) {
374 nsAutoString integrity
;
375 GetAttr(nsGkAtoms::integrity
, integrity
);
377 nsCOMPtr
<nsIURI
> uri
= GetURI();
378 nsCOMPtr
<nsIPrincipal
> prin
= mTriggeringPrincipal
;
381 nsString
* cspNonce
= static_cast<nsString
*>(GetProperty(nsGkAtoms::nonce
));
386 return Some(SheetInfo
{
391 MakeAndAddRef
<ReferrerInfo
>(*this),
397 alternate
? HasAlternateRel::Yes
: HasAlternateRel::No
,
399 mExplicitlyEnabled
? IsExplicitlyEnabled::Yes
: IsExplicitlyEnabled::No
,
404 void HTMLLinkElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
405 size_t* aNodeSize
) const {
406 nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
407 if (nsCOMPtr
<nsISizeOf
> iface
= do_QueryInterface(mCachedURI
)) {
408 *aNodeSize
+= iface
->SizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
412 JSObject
* HTMLLinkElement::WrapNode(JSContext
* aCx
,
413 JS::Handle
<JSObject
*> aGivenProto
) {
414 return HTMLLinkElement_Binding::Wrap(aCx
, this, aGivenProto
);
417 void HTMLLinkElement::GetAs(nsAString
& aResult
) {
418 GetEnumAttr(nsGkAtoms::as
, "", aResult
);
421 void HTMLLinkElement::GetContentPolicyMimeTypeMedia(
422 nsAttrValue
& aAsAttr
, nsContentPolicyType
& aPolicyType
, nsString
& aMimeType
,
425 GetAttr(nsGkAtoms::as
, as
);
426 net::ParseAsValue(as
, aAsAttr
);
427 aPolicyType
= net::AsValueToContentPolicy(aAsAttr
);
430 GetAttr(nsGkAtoms::type
, type
);
431 nsAutoString notUsed
;
432 nsContentUtils::SplitMimeType(type
, aMimeType
, notUsed
);
434 GetAttr(nsGkAtoms::media
, aMedia
);
437 void HTMLLinkElement::
438 TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender() {
439 MOZ_ASSERT(IsInComposedDoc());
440 if (!HasAttr(nsGkAtoms::href
)) {
445 if (!GetAttr(nsGkAtoms::rel
, rel
)) {
449 if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
453 uint32_t linkTypes
= ParseLinkTypes(rel
);
455 if ((linkTypes
& ePREFETCH
) || (linkTypes
& eNEXT
)) {
456 nsCOMPtr
<nsIPrefetchService
> prefetchService(
457 components::Prefetch::Service());
458 if (prefetchService
) {
459 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
460 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*this);
461 prefetchService
->PrefetchURI(uri
, referrerInfo
, this,
462 linkTypes
& ePREFETCH
);
468 if (linkTypes
& ePRELOAD
) {
469 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
470 nsContentPolicyType policyType
;
473 nsAutoString mimeType
;
475 GetContentPolicyMimeTypeMedia(asAttr
, policyType
, mimeType
, media
);
477 if (policyType
== nsIContentPolicy::TYPE_INVALID
||
478 !net::CheckPreloadAttrs(asAttr
, mimeType
, media
, OwnerDoc())) {
479 // Ignore preload with a wrong or empty as attribute.
480 net::WarnIgnoredPreload(*OwnerDoc(), *uri
);
484 StartPreload(policyType
);
489 if (linkTypes
& eMODULE_PRELOAD
) {
490 ScriptLoader
* scriptLoader
= OwnerDoc()->ScriptLoader();
491 ModuleLoader
* moduleLoader
= scriptLoader
->GetModuleLoader();
494 // For the print preview documents, at this moment it doesn't have module
495 // loader yet, as the (print preview) document is not attached to the
496 // nsIDocumentViewer yet, so it doesn't have the GlobalObject.
497 // Also, the script elements won't be processed as they are also cloned
498 // from the original document.
499 // So we simply bail out if the module loader is null.
503 if (!StaticPrefs::network_modulepreload()) {
504 // Keep behavior from https://phabricator.services.mozilla.com/D149371,
505 // prior to main implementation of modulepreload
506 moduleLoader
->DisallowImportMaps();
510 // https://html.spec.whatwg.org/multipage/semantics.html#processing-the-media-attribute
511 // TODO: apply this check for all linkTypes
513 if (GetAttr(nsGkAtoms::media
, media
)) {
514 RefPtr
<mozilla::dom::MediaList
> mediaList
=
515 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(media
));
516 if (!mediaList
->Matches(*OwnerDoc())) {
521 // TODO: per spec, apply this check for ePREFETCH as well
522 if (!HasNonEmptyAttr(nsGkAtoms::href
)) {
527 GetAttr(nsGkAtoms::as
, as
);
529 if (!net::IsScriptLikeOrInvalid(as
)) {
530 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
= new AsyncEventDispatcher(
531 this, u
"error"_ns
, CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
532 asyncDispatcher
->PostDOMEvent();
536 nsCOMPtr
<nsIURI
> uri
= GetURI();
541 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-modulepreload-module-script-graph
542 // Step 1. Disallow further import maps given settings object.
543 moduleLoader
->DisallowImportMaps();
545 StartPreload(nsIContentPolicy::TYPE_SCRIPT
);
549 if (linkTypes
& ePRECONNECT
) {
550 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
551 OwnerDoc()->MaybePreconnect(
552 uri
, AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin
)));
557 if (linkTypes
& eDNS_PREFETCH
) {
558 TryDNSPrefetch(*this);
562 void HTMLLinkElement::UpdatePreload(nsAtom
* aName
, const nsAttrValue
* aValue
,
563 const nsAttrValue
* aOldValue
) {
564 MOZ_ASSERT(IsInComposedDoc());
566 if (!HasAttr(nsGkAtoms::href
)) {
571 if (!GetAttr(nsGkAtoms::rel
, rel
)) {
575 if (!nsContentUtils::PrefetchPreloadEnabled(OwnerDoc()->GetDocShell())) {
579 uint32_t linkTypes
= ParseLinkTypes(rel
);
581 if (!(linkTypes
& ePRELOAD
)) {
585 nsCOMPtr
<nsIURI
> uri
= GetURI();
591 nsContentPolicyType asPolicyType
;
592 nsAutoString mimeType
;
594 GetContentPolicyMimeTypeMedia(asAttr
, asPolicyType
, mimeType
, media
);
596 if (asPolicyType
== nsIContentPolicy::TYPE_INVALID
||
597 !net::CheckPreloadAttrs(asAttr
, mimeType
, media
, OwnerDoc())) {
598 // Ignore preload with a wrong or empty as attribute, but be sure to cancel
600 CancelPrefetchOrPreload();
601 net::WarnIgnoredPreload(*OwnerDoc(), *uri
);
605 if (aName
== nsGkAtoms::crossorigin
) {
606 CORSMode corsMode
= AttrValueToCORSMode(aValue
);
607 CORSMode oldCorsMode
= AttrValueToCORSMode(aOldValue
);
608 if (corsMode
!= oldCorsMode
) {
609 CancelPrefetchOrPreload();
610 StartPreload(asPolicyType
);
615 nsContentPolicyType oldPolicyType
;
617 if (aName
== nsGkAtoms::as
) {
619 oldPolicyType
= net::AsValueToContentPolicy(*aOldValue
);
620 if (!net::CheckPreloadAttrs(*aOldValue
, mimeType
, media
, OwnerDoc())) {
621 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
624 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
626 } else if (aName
== nsGkAtoms::type
) {
627 nsAutoString oldType
;
628 nsAutoString notUsed
;
630 aOldValue
->ToString(oldType
);
632 nsAutoString oldMimeType
;
633 nsContentUtils::SplitMimeType(oldType
, oldMimeType
, notUsed
);
634 if (net::CheckPreloadAttrs(asAttr
, oldMimeType
, media
, OwnerDoc())) {
635 oldPolicyType
= asPolicyType
;
637 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
640 MOZ_ASSERT(aName
== nsGkAtoms::media
);
641 nsAutoString oldMedia
;
643 aOldValue
->ToString(oldMedia
);
645 if (net::CheckPreloadAttrs(asAttr
, mimeType
, oldMedia
, OwnerDoc())) {
646 oldPolicyType
= asPolicyType
;
648 oldPolicyType
= nsIContentPolicy::TYPE_INVALID
;
652 if (asPolicyType
!= oldPolicyType
&&
653 oldPolicyType
!= nsIContentPolicy::TYPE_INVALID
) {
654 CancelPrefetchOrPreload();
657 // Trigger a new preload if the policy type has changed.
658 if (asPolicyType
!= oldPolicyType
) {
659 StartPreload(asPolicyType
);
663 void HTMLLinkElement::CancelPrefetchOrPreload() {
666 nsCOMPtr
<nsIPrefetchService
> prefetchService(components::Prefetch::Service());
667 if (prefetchService
) {
668 if (nsCOMPtr
<nsIURI
> uri
= GetURI()) {
669 prefetchService
->CancelPrefetchPreloadURI(uri
, this);
674 void HTMLLinkElement::StartPreload(nsContentPolicyType aPolicyType
) {
675 MOZ_ASSERT(!mPreload
, "Forgot to cancel the running preload");
676 RefPtr
<PreloaderBase
> preload
=
677 OwnerDoc()->Preloads().PreloadLinkElement(this, aPolicyType
);
678 mPreload
= preload
.get();
681 void HTMLLinkElement::CancelPreload() {
683 // This will cancel the loading channel if this was the last referred node
684 // and the preload is not used up until now to satisfy a regular tag load
686 mPreload
->RemoveLinkPreloadNode(this);
691 bool HTMLLinkElement::IsCSSMimeTypeAttributeForLinkElement(
692 const Element
& aSelf
) {
693 // Processing the type attribute per
694 // https://html.spec.whatwg.org/multipage/semantics.html#processing-the-type-attribute
695 // for HTML link elements.
697 nsAutoString mimeType
;
698 nsAutoString notUsed
;
699 aSelf
.GetAttr(nsGkAtoms::type
, type
);
700 nsContentUtils::SplitMimeType(type
, mimeType
, notUsed
);
701 return mimeType
.IsEmpty() || mimeType
.LowerCaseEqualsLiteral("text/css");
704 nsDOMTokenList
* HTMLLinkElement::Blocking() {
707 new nsDOMTokenList(this, nsGkAtoms::blocking
, sSupportedBlockingValues
);
712 bool HTMLLinkElement::IsPotentiallyRenderBlocking() {
713 return BlockingContainsRender();
715 // TODO: handle implicitly potentially render blocking
716 // https://html.spec.whatwg.org/#implicitly-potentially-render-blocking
717 // The default type for resources given by the stylesheet keyword is text/css.
718 // A link element of this type is implicitly potentially render-blocking if
719 // the element was created by its node document's parser.
722 } // namespace mozilla::dom