1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "PreloadService.h"
8 #include "FetchPreloader.h"
9 #include "mozilla/AsyncEventDispatcher.h"
10 #include "mozilla/dom/HTMLLinkElement.h"
11 #include "mozilla/dom/ScriptLoader.h"
12 #include "mozilla/FontPreloader.h"
13 #include "mozilla/StaticPrefs_network.h"
14 #include "nsIReferrerInfo.h"
15 #include "nsNetUtil.h"
19 bool PreloadService::RegisterPreload(PreloadHashKey
* aKey
,
20 PreloaderBase
* aPreload
) {
21 if (PreloadExists(aKey
)) {
25 mPreloads
.Put(aKey
, RefPtr
{aPreload
});
29 void PreloadService::DeregisterPreload(PreloadHashKey
* aKey
) {
30 mPreloads
.Remove(aKey
);
33 void PreloadService::ClearAllPreloads() { mPreloads
.Clear(); }
35 bool PreloadService::PreloadExists(PreloadHashKey
* aKey
) {
37 mPreloads
.GetWeak(aKey
, &found
);
41 already_AddRefed
<PreloaderBase
> PreloadService::LookupPreload(
42 PreloadHashKey
* aKey
) const {
43 return mPreloads
.Get(aKey
);
46 already_AddRefed
<nsIURI
> PreloadService::GetPreloadURI(const nsAString
& aURL
) {
47 nsIURI
* base
= BaseURIForPreload();
48 auto encoding
= mDocument
->GetDocumentCharacterSet();
51 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aURL
, encoding
, base
);
59 already_AddRefed
<PreloaderBase
> PreloadService::PreloadLinkElement(
60 dom::HTMLLinkElement
* aLinkElement
, nsContentPolicyType aPolicyType
,
61 nsIReferrerInfo
* aReferrerInfo
) {
62 if (!StaticPrefs::network_preload()) {
66 if (!CheckReferrerURIScheme(aReferrerInfo
)) {
70 if (aPolicyType
== nsIContentPolicy::TYPE_INVALID
) {
71 NotifyNodeEvent(aLinkElement
, false);
75 nsAutoString as
, charset
, crossOrigin
, integrity
, referrerPolicyAttr
, srcset
,
78 nsCOMPtr
<nsIURI
> uri
= aLinkElement
->GetURI();
79 aLinkElement
->GetAs(as
);
80 aLinkElement
->GetCharset(charset
);
81 aLinkElement
->GetImageSrcset(srcset
);
82 aLinkElement
->GetImageSizes(sizes
);
83 aLinkElement
->GetHref(url
);
84 aLinkElement
->GetCrossOrigin(crossOrigin
);
85 aLinkElement
->GetIntegrity(integrity
);
86 aLinkElement
->GetReferrerPolicy(referrerPolicyAttr
);
87 auto referrerPolicy
= PreloadReferrerPolicy(referrerPolicyAttr
);
88 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
89 dom::ReferrerInfo::CreateFromDocumentAndPolicyOverride(mDocument
,
91 dom::DOMString domType
;
92 aLinkElement
->GetType(domType
);
93 domType
.ToString(type
);
95 RefPtr
<PreloaderBase
> preload
= PreloadOrCoalesce(
96 uri
, url
, aPolicyType
, as
, type
, charset
, srcset
, sizes
, integrity
,
97 crossOrigin
, referrerPolicy
, referrerPolicyAttr
, referrerInfo
);
100 NotifyNodeEvent(aLinkElement
, false);
104 preload
->AddLinkPreloadNode(aLinkElement
);
105 return preload
.forget();
108 already_AddRefed
<PreloaderBase
> PreloadService::PreloadLinkHeader(
109 nsIURI
* aURI
, const nsAString
& aURL
, nsContentPolicyType aPolicyType
,
110 const nsAString
& aAs
, const nsAString
& aType
, const nsAString
& aIntegrity
,
111 const nsAString
& aSrcset
, const nsAString
& aSizes
, const nsAString
& aCORS
,
112 const nsAString
& aReferrerPolicy
, nsIReferrerInfo
* aReferrerInfo
) {
113 if (!StaticPrefs::network_preload()) {
117 if (!CheckReferrerURIScheme(aReferrerInfo
)) {
121 if (aPolicyType
== nsIContentPolicy::TYPE_INVALID
) {
125 auto referrerPolicy
= PreloadReferrerPolicy(aReferrerPolicy
);
126 return PreloadOrCoalesce(aURI
, aURL
, aPolicyType
, aAs
, aType
, EmptyString(),
127 aSrcset
, aSizes
, aIntegrity
, aCORS
, referrerPolicy
,
128 aReferrerPolicy
, aReferrerInfo
);
131 already_AddRefed
<PreloaderBase
> PreloadService::PreloadOrCoalesce(
132 nsIURI
* aURI
, const nsAString
& aURL
, nsContentPolicyType aPolicyType
,
133 const nsAString
& aAs
, const nsAString
& aType
, const nsAString
& aCharset
,
134 const nsAString
& aSrcset
, const nsAString
& aSizes
,
135 const nsAString
& aIntegrity
, const nsAString
& aCORS
,
136 dom::ReferrerPolicy aReferrerPolicy
, const nsAString
& aReferrerPolicyAttr
,
137 nsIReferrerInfo
* aReferrerInfo
) {
138 bool isImgSet
= false;
139 PreloadHashKey preloadKey
;
140 nsCOMPtr
<nsIURI
> uri
= aURI
;
142 if (aAs
.LowerCaseEqualsASCII("script")) {
144 PreloadHashKey::CreateAsScript(uri
, aCORS
, aType
, aReferrerPolicy
);
145 } else if (aAs
.LowerCaseEqualsASCII("style")) {
146 preloadKey
= PreloadHashKey::CreateAsStyle(
147 uri
, mDocument
->NodePrincipal(), aReferrerInfo
,
148 dom::Element::StringToCORSMode(aCORS
),
149 css::eAuthorSheetFeatures
/* see Loader::LoadSheet */);
150 } else if (aAs
.LowerCaseEqualsASCII("image")) {
151 uri
= mDocument
->ResolvePreloadImage(BaseURIForPreload(), aURL
, aSrcset
,
157 preloadKey
= PreloadHashKey::CreateAsImage(
158 uri
, mDocument
->NodePrincipal(), dom::Element::StringToCORSMode(aCORS
),
160 } else if (aAs
.LowerCaseEqualsASCII("font")) {
161 preloadKey
= PreloadHashKey::CreateAsFont(
162 uri
, dom::Element::StringToCORSMode(aCORS
), aReferrerPolicy
);
163 } else if (aAs
.LowerCaseEqualsASCII("fetch")) {
164 preloadKey
= PreloadHashKey::CreateAsFetch(
165 uri
, dom::Element::StringToCORSMode(aCORS
), aReferrerPolicy
);
170 RefPtr
<PreloaderBase
> preload
= LookupPreload(&preloadKey
);
172 if (aAs
.LowerCaseEqualsASCII("script")) {
173 PreloadScript(uri
, aType
, aCharset
, aCORS
, aReferrerPolicyAttr
,
174 aIntegrity
, true /* isInHead - TODO */);
175 } else if (aAs
.LowerCaseEqualsASCII("style")) {
176 PreloadStyle(uri
, aCharset
, aCORS
, aReferrerPolicyAttr
, aIntegrity
);
177 } else if (aAs
.LowerCaseEqualsASCII("image")) {
178 PreloadImage(uri
, aCORS
, aReferrerPolicyAttr
, isImgSet
);
179 } else if (aAs
.LowerCaseEqualsASCII("font")) {
180 PreloadFont(uri
, aCORS
, aReferrerPolicyAttr
);
181 } else if (aAs
.LowerCaseEqualsASCII("fetch")) {
182 PreloadFetch(uri
, aCORS
, aReferrerPolicyAttr
);
185 preload
= LookupPreload(&preloadKey
);
191 return preload
.forget();
194 void PreloadService::PreloadScript(nsIURI
* aURI
, const nsAString
& aType
,
195 const nsAString
& aCharset
,
196 const nsAString
& aCrossOrigin
,
197 const nsAString
& aReferrerPolicy
,
198 const nsAString
& aIntegrity
,
199 bool aScriptFromHead
) {
200 mDocument
->ScriptLoader()->PreloadURI(
201 aURI
, aCharset
, aType
, aCrossOrigin
, aIntegrity
, aScriptFromHead
, false,
202 false, false, true, PreloadReferrerPolicy(aReferrerPolicy
));
205 void PreloadService::PreloadStyle(nsIURI
* aURI
, const nsAString
& aCharset
,
206 const nsAString
& aCrossOrigin
,
207 const nsAString
& aReferrerPolicy
,
208 const nsAString
& aIntegrity
) {
209 mDocument
->PreloadStyle(aURI
, Encoding::ForLabel(aCharset
), aCrossOrigin
,
210 PreloadReferrerPolicy(aReferrerPolicy
), aIntegrity
,
214 void PreloadService::PreloadImage(nsIURI
* aURI
, const nsAString
& aCrossOrigin
,
215 const nsAString
& aImageReferrerPolicy
,
217 mDocument
->PreLoadImage(aURI
, aCrossOrigin
,
218 PreloadReferrerPolicy(aImageReferrerPolicy
),
222 void PreloadService::PreloadFont(nsIURI
* aURI
, const nsAString
& aCrossOrigin
,
223 const nsAString
& aReferrerPolicy
) {
224 CORSMode cors
= dom::Element::StringToCORSMode(aCrossOrigin
);
225 dom::ReferrerPolicy referrerPolicy
= PreloadReferrerPolicy(aReferrerPolicy
);
226 auto key
= PreloadHashKey::CreateAsFont(aURI
, cors
, referrerPolicy
);
228 // * Bug 1618549: Depending on where we decide to do the deduplication, we may
229 // want to check if the font is already being preloaded here.
231 RefPtr
<FontPreloader
> preloader
= new FontPreloader();
232 preloader
->OpenChannel(&key
, aURI
, cors
, referrerPolicy
, mDocument
);
235 void PreloadService::PreloadFetch(nsIURI
* aURI
, const nsAString
& aCrossOrigin
,
236 const nsAString
& aReferrerPolicy
) {
237 CORSMode cors
= dom::Element::StringToCORSMode(aCrossOrigin
);
238 dom::ReferrerPolicy referrerPolicy
= PreloadReferrerPolicy(aReferrerPolicy
);
239 auto key
= PreloadHashKey::CreateAsFetch(aURI
, cors
, referrerPolicy
);
241 // * Bug 1618549: Depending on where we decide to do the deduplication, we may
242 // want to check if a fetch is already being preloaded here.
244 RefPtr
<FetchPreloader
> preloader
= new FetchPreloader();
245 preloader
->OpenChannel(&key
, aURI
, cors
, referrerPolicy
, mDocument
);
249 void PreloadService::NotifyNodeEvent(nsINode
* aNode
, bool aSuccess
) {
250 if (!aNode
->IsInComposedDoc()) {
254 // We don't dispatch synchronously since |node| might be in a DocGroup
255 // that we're not allowed to touch. (Our network request happens in the
256 // DocGroup of one of the mSources nodes--not necessarily this one).
258 RefPtr
<AsyncEventDispatcher
> dispatcher
= new AsyncEventDispatcher(
259 aNode
, aSuccess
? NS_LITERAL_STRING("load") : NS_LITERAL_STRING("error"),
262 dispatcher
->RequireNodeInDocument();
263 dispatcher
->PostDOMEvent();
266 dom::ReferrerPolicy
PreloadService::PreloadReferrerPolicy(
267 const nsAString
& aReferrerPolicy
) {
268 dom::ReferrerPolicy referrerPolicy
=
269 dom::ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy
);
270 if (referrerPolicy
== dom::ReferrerPolicy::_empty
) {
271 referrerPolicy
= mDocument
->GetPreloadReferrerInfo()->ReferrerPolicy();
274 return referrerPolicy
;
277 bool PreloadService::CheckReferrerURIScheme(nsIReferrerInfo
* aReferrerInfo
) {
278 if (!aReferrerInfo
) {
282 nsCOMPtr
<nsIURI
> referrer
= aReferrerInfo
->GetOriginalReferrer();
286 if (!referrer
->SchemeIs("http") && !referrer
->SchemeIs("https")) {
293 nsIURI
* PreloadService::BaseURIForPreload() {
294 nsIURI
* documentURI
= mDocument
->GetDocumentURI();
295 nsIURI
* documentBaseURI
= mDocument
->GetDocBaseURI();
296 return (documentURI
== documentBaseURI
)
297 ? (mSpeculationBaseURI
? mSpeculationBaseURI
.get() : documentURI
)
301 } // namespace mozilla