Bug 1641886 [wpt PR 23851] - Support interpolating contain-intrinsic-size, a=testonly
[gecko.git] / uriloader / preload / PreloadService.cpp
blob4e8ff1defebcb27100e615738e354dd831e2f7e1
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"
17 namespace mozilla {
19 bool PreloadService::RegisterPreload(PreloadHashKey* aKey,
20 PreloaderBase* aPreload) {
21 if (PreloadExists(aKey)) {
22 return false;
25 mPreloads.Put(aKey, RefPtr{aPreload});
26 return true;
29 void PreloadService::DeregisterPreload(PreloadHashKey* aKey) {
30 mPreloads.Remove(aKey);
33 void PreloadService::ClearAllPreloads() { mPreloads.Clear(); }
35 bool PreloadService::PreloadExists(PreloadHashKey* aKey) {
36 bool found;
37 mPreloads.GetWeak(aKey, &found);
38 return 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();
50 nsCOMPtr<nsIURI> uri;
51 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, encoding, base);
52 if (NS_FAILED(rv)) {
53 return nullptr;
56 return uri.forget();
59 already_AddRefed<PreloaderBase> PreloadService::PreloadLinkElement(
60 dom::HTMLLinkElement* aLinkElement, nsContentPolicyType aPolicyType,
61 nsIReferrerInfo* aReferrerInfo) {
62 if (!StaticPrefs::network_preload()) {
63 return nullptr;
66 if (!CheckReferrerURIScheme(aReferrerInfo)) {
67 return nullptr;
70 if (aPolicyType == nsIContentPolicy::TYPE_INVALID) {
71 NotifyNodeEvent(aLinkElement, false);
72 return nullptr;
75 nsAutoString as, charset, crossOrigin, integrity, referrerPolicyAttr, srcset,
76 sizes, type, url;
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,
90 referrerPolicy);
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);
99 if (!preload) {
100 NotifyNodeEvent(aLinkElement, false);
101 return nullptr;
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()) {
114 return nullptr;
117 if (!CheckReferrerURIScheme(aReferrerInfo)) {
118 return nullptr;
121 if (aPolicyType == nsIContentPolicy::TYPE_INVALID) {
122 return nullptr;
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")) {
143 preloadKey =
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,
152 aSizes, &isImgSet);
153 if (!uri) {
154 return nullptr;
157 preloadKey = PreloadHashKey::CreateAsImage(
158 uri, mDocument->NodePrincipal(), dom::Element::StringToCORSMode(aCORS),
159 aReferrerPolicy);
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);
166 } else {
167 return nullptr;
170 RefPtr<PreloaderBase> preload = LookupPreload(&preloadKey);
171 if (!preload) {
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);
186 if (!preload) {
187 return nullptr;
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,
211 true);
214 void PreloadService::PreloadImage(nsIURI* aURI, const nsAString& aCrossOrigin,
215 const nsAString& aImageReferrerPolicy,
216 bool aIsImgSet) {
217 mDocument->PreLoadImage(aURI, aCrossOrigin,
218 PreloadReferrerPolicy(aImageReferrerPolicy),
219 aIsImgSet, true);
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);
248 // static
249 void PreloadService::NotifyNodeEvent(nsINode* aNode, bool aSuccess) {
250 if (!aNode->IsInComposedDoc()) {
251 return;
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"),
260 CanBubble::eNo);
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) {
279 return false;
282 nsCOMPtr<nsIURI> referrer = aReferrerInfo->GetOriginalReferrer();
283 if (!referrer) {
284 return false;
286 if (!referrer->SchemeIs("http") && !referrer->SchemeIs("https")) {
287 return false;
290 return true;
293 nsIURI* PreloadService::BaseURIForPreload() {
294 nsIURI* documentURI = mDocument->GetDocumentURI();
295 nsIURI* documentBaseURI = mDocument->GetDocBaseURI();
296 return (documentURI == documentBaseURI)
297 ? (mSpeculationBaseURI ? mSpeculationBaseURI.get() : documentURI)
298 : documentBaseURI;
301 } // namespace mozilla