Bug 1829125 - Align the PHC area to the jemalloc chunk size r=glandium
[gecko.git] / uriloader / preload / PreloadService.cpp
blob6766dc5799fe7c42156f46165e85275b886b91d0
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 "PreloaderBase.h"
10 #include "mozilla/AsyncEventDispatcher.h"
11 #include "mozilla/dom/HTMLLinkElement.h"
12 #include "mozilla/dom/ScriptLoader.h"
13 #include "mozilla/dom/ReferrerInfo.h"
14 #include "mozilla/Encoding.h"
15 #include "mozilla/FontPreloader.h"
16 #include "mozilla/StaticPrefs_network.h"
17 #include "nsNetUtil.h"
19 namespace mozilla {
21 PreloadService::PreloadService(dom::Document* aDoc) : mDocument(aDoc) {}
22 PreloadService::~PreloadService() = default;
24 bool PreloadService::RegisterPreload(const PreloadHashKey& aKey,
25 PreloaderBase* aPreload) {
26 return mPreloads.WithEntryHandle(aKey, [&](auto&& lookup) {
27 if (lookup) {
28 lookup.Data() = aPreload;
29 return true;
31 lookup.Insert(aPreload);
32 return false;
33 });
36 void PreloadService::DeregisterPreload(const PreloadHashKey& aKey) {
37 mPreloads.Remove(aKey);
40 void PreloadService::ClearAllPreloads() { mPreloads.Clear(); }
42 bool PreloadService::PreloadExists(const PreloadHashKey& aKey) {
43 return mPreloads.Contains(aKey);
46 already_AddRefed<PreloaderBase> PreloadService::LookupPreload(
47 const PreloadHashKey& aKey) const {
48 return mPreloads.Get(aKey);
51 already_AddRefed<nsIURI> PreloadService::GetPreloadURI(const nsAString& aURL) {
52 nsIURI* base = BaseURIForPreload();
53 auto encoding = mDocument->GetDocumentCharacterSet();
55 nsCOMPtr<nsIURI> uri;
56 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, encoding, base);
57 if (NS_FAILED(rv)) {
58 return nullptr;
61 return uri.forget();
64 already_AddRefed<PreloaderBase> PreloadService::PreloadLinkElement(
65 dom::HTMLLinkElement* aLinkElement, nsContentPolicyType aPolicyType) {
66 if (aPolicyType == nsIContentPolicy::TYPE_INVALID) {
67 MOZ_ASSERT_UNREACHABLE("Caller should check");
68 return nullptr;
71 if (!StaticPrefs::network_preload()) {
72 return nullptr;
75 nsAutoString as, charset, crossOrigin, integrity, referrerPolicy, rel, srcset,
76 sizes, type, url;
78 nsCOMPtr<nsIURI> uri = aLinkElement->GetURI();
79 aLinkElement->GetCharset(charset);
80 aLinkElement->GetImageSrcset(srcset);
81 aLinkElement->GetImageSizes(sizes);
82 aLinkElement->GetHref(url);
83 aLinkElement->GetCrossOrigin(crossOrigin);
84 aLinkElement->GetIntegrity(integrity);
85 aLinkElement->GetReferrerPolicy(referrerPolicy);
86 aLinkElement->GetRel(rel);
88 if (rel.LowerCaseEqualsASCII("modulepreload")) {
89 as = u"script"_ns;
90 type = u"module"_ns;
91 } else {
92 aLinkElement->GetAs(as);
93 aLinkElement->GetType(type);
96 auto result = PreloadOrCoalesce(uri, url, aPolicyType, as, type, charset,
97 srcset, sizes, integrity, crossOrigin,
98 referrerPolicy, /* aFromHeader = */ false, 0);
100 if (!result.mPreloader) {
101 NotifyNodeEvent(aLinkElement, result.mAlreadyComplete);
102 return nullptr;
105 result.mPreloader->AddLinkPreloadNode(aLinkElement);
106 return result.mPreloader.forget();
109 void PreloadService::PreloadLinkHeader(
110 nsIURI* aURI, const nsAString& aURL, nsContentPolicyType aPolicyType,
111 const nsAString& aAs, const nsAString& aType, const nsAString& aIntegrity,
112 const nsAString& aSrcset, const nsAString& aSizes, const nsAString& aCORS,
113 const nsAString& aReferrerPolicy, uint64_t aEarlyHintPreloaderId) {
114 if (aPolicyType == nsIContentPolicy::TYPE_INVALID) {
115 MOZ_ASSERT_UNREACHABLE("Caller should check");
116 return;
119 if (!StaticPrefs::network_preload()) {
120 return;
123 PreloadOrCoalesce(aURI, aURL, aPolicyType, aAs, aType, u""_ns, aSrcset,
124 aSizes, aIntegrity, aCORS, aReferrerPolicy,
125 /* aFromHeader = */ true, aEarlyHintPreloaderId);
128 PreloadService::PreloadOrCoalesceResult PreloadService::PreloadOrCoalesce(
129 nsIURI* aURI, const nsAString& aURL, nsContentPolicyType aPolicyType,
130 const nsAString& aAs, const nsAString& aType, const nsAString& aCharset,
131 const nsAString& aSrcset, const nsAString& aSizes,
132 const nsAString& aIntegrity, const nsAString& aCORS,
133 const nsAString& aReferrerPolicy, bool aFromHeader,
134 uint64_t aEarlyHintPreloaderId) {
135 if (!aURI) {
136 MOZ_ASSERT_UNREACHABLE("Should not pass null nsIURI");
137 return {nullptr, false};
140 bool isImgSet = false;
141 PreloadHashKey preloadKey;
142 nsCOMPtr<nsIURI> uri = aURI;
144 if (aAs.LowerCaseEqualsASCII("script")) {
145 preloadKey = PreloadHashKey::CreateAsScript(uri, aCORS, aType);
146 } else if (aAs.LowerCaseEqualsASCII("style")) {
147 preloadKey = PreloadHashKey::CreateAsStyle(
148 uri, mDocument->NodePrincipal(), 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, false};
157 preloadKey = PreloadHashKey::CreateAsImage(
158 uri, mDocument->NodePrincipal(), dom::Element::StringToCORSMode(aCORS));
159 } else if (aAs.LowerCaseEqualsASCII("font")) {
160 preloadKey = PreloadHashKey::CreateAsFont(
161 uri, dom::Element::StringToCORSMode(aCORS));
162 } else if (aAs.LowerCaseEqualsASCII("fetch")) {
163 preloadKey = PreloadHashKey::CreateAsFetch(
164 uri, dom::Element::StringToCORSMode(aCORS));
165 } else {
166 return {nullptr, false};
169 if (RefPtr<PreloaderBase> preload = LookupPreload(preloadKey)) {
170 return {std::move(preload), false};
173 if (aAs.LowerCaseEqualsASCII("script")) {
174 PreloadScript(uri, aType, aCharset, aCORS, aReferrerPolicy, aIntegrity,
175 true /* isInHead - TODO */, aEarlyHintPreloaderId);
176 } else if (aAs.LowerCaseEqualsASCII("style")) {
177 auto status = mDocument->PreloadStyle(
178 aURI, Encoding::ForLabel(aCharset), aCORS,
179 PreloadReferrerPolicy(aReferrerPolicy), aIntegrity,
180 aFromHeader ? css::StylePreloadKind::FromLinkRelPreloadHeader
181 : css::StylePreloadKind::FromLinkRelPreloadElement,
182 aEarlyHintPreloaderId);
183 switch (status) {
184 case dom::SheetPreloadStatus::AlreadyComplete:
185 return {nullptr, /* already_complete = */ true};
186 case dom::SheetPreloadStatus::Errored:
187 case dom::SheetPreloadStatus::InProgress:
188 break;
190 } else if (aAs.LowerCaseEqualsASCII("image")) {
191 PreloadImage(uri, aCORS, aReferrerPolicy, isImgSet, aEarlyHintPreloaderId);
192 } else if (aAs.LowerCaseEqualsASCII("font")) {
193 PreloadFont(uri, aCORS, aReferrerPolicy, aEarlyHintPreloaderId);
194 } else if (aAs.LowerCaseEqualsASCII("fetch")) {
195 PreloadFetch(uri, aCORS, aReferrerPolicy, aEarlyHintPreloaderId);
198 RefPtr<PreloaderBase> preload = LookupPreload(preloadKey);
199 if (preload && aEarlyHintPreloaderId) {
200 preload->SetForEarlyHints();
203 return {preload, false};
206 void PreloadService::PreloadScript(nsIURI* aURI, const nsAString& aType,
207 const nsAString& aCharset,
208 const nsAString& aCrossOrigin,
209 const nsAString& aReferrerPolicy,
210 const nsAString& aIntegrity,
211 bool aScriptFromHead,
212 uint64_t aEarlyHintPreloaderId) {
213 mDocument->ScriptLoader()->PreloadURI(
214 aURI, aCharset, aType, aCrossOrigin, aIntegrity, aScriptFromHead, false,
215 false, false, true, PreloadReferrerPolicy(aReferrerPolicy),
216 aEarlyHintPreloaderId);
219 void PreloadService::PreloadImage(nsIURI* aURI, const nsAString& aCrossOrigin,
220 const nsAString& aImageReferrerPolicy,
221 bool aIsImgSet,
222 uint64_t aEarlyHintPreloaderId) {
223 mDocument->PreLoadImage(aURI, aCrossOrigin,
224 PreloadReferrerPolicy(aImageReferrerPolicy),
225 aIsImgSet, true, aEarlyHintPreloaderId);
228 void PreloadService::PreloadFont(nsIURI* aURI, const nsAString& aCrossOrigin,
229 const nsAString& aReferrerPolicy,
230 uint64_t aEarlyHintPreloaderId) {
231 CORSMode cors = dom::Element::StringToCORSMode(aCrossOrigin);
232 auto key = PreloadHashKey::CreateAsFont(aURI, cors);
234 if (PreloadExists(key)) {
235 return;
238 RefPtr<FontPreloader> preloader = new FontPreloader();
239 dom::ReferrerPolicy referrerPolicy = PreloadReferrerPolicy(aReferrerPolicy);
240 preloader->OpenChannel(key, aURI, cors, referrerPolicy, mDocument,
241 aEarlyHintPreloaderId);
244 void PreloadService::PreloadFetch(nsIURI* aURI, const nsAString& aCrossOrigin,
245 const nsAString& aReferrerPolicy,
246 uint64_t aEarlyHintPreloaderId) {
247 CORSMode cors = dom::Element::StringToCORSMode(aCrossOrigin);
248 auto key = PreloadHashKey::CreateAsFetch(aURI, cors);
250 if (PreloadExists(key)) {
251 return;
254 RefPtr<FetchPreloader> preloader = new FetchPreloader();
255 dom::ReferrerPolicy referrerPolicy = PreloadReferrerPolicy(aReferrerPolicy);
256 preloader->OpenChannel(key, aURI, cors, referrerPolicy, mDocument,
257 aEarlyHintPreloaderId);
260 // static
261 void PreloadService::NotifyNodeEvent(nsINode* aNode, bool aSuccess) {
262 if (!aNode->IsInComposedDoc()) {
263 return;
266 // We don't dispatch synchronously since |node| might be in a DocGroup
267 // that we're not allowed to touch. (Our network request happens in the
268 // DocGroup of one of the mSources nodes--not necessarily this one).
270 RefPtr<AsyncEventDispatcher> dispatcher = new AsyncEventDispatcher(
271 aNode, aSuccess ? u"load"_ns : u"error"_ns, CanBubble::eNo);
273 dispatcher->RequireNodeInDocument();
274 dispatcher->PostDOMEvent();
277 dom::ReferrerPolicy PreloadService::PreloadReferrerPolicy(
278 const nsAString& aReferrerPolicy) {
279 dom::ReferrerPolicy referrerPolicy =
280 dom::ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy);
281 if (referrerPolicy == dom::ReferrerPolicy::_empty) {
282 referrerPolicy = mDocument->GetPreloadReferrerInfo()->ReferrerPolicy();
285 return referrerPolicy;
288 nsIURI* PreloadService::BaseURIForPreload() {
289 nsIURI* documentURI = mDocument->GetDocumentURI();
290 nsIURI* documentBaseURI = mDocument->GetDocBaseURI();
291 return (documentURI == documentBaseURI)
292 ? (mSpeculationBaseURI ? mSpeculationBaseURI.get() : documentURI)
293 : documentBaseURI;
296 } // namespace mozilla