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 #ifndef js_loader_ScriptLoadRequest_h
8 #define js_loader_ScriptLoadRequest_h
10 #include "js/experimental/JSStencil.h"
11 #include "js/RootingAPI.h"
12 #include "js/SourceText.h"
13 #include "js/TypeDecls.h"
14 #include "mozilla/Atomics.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/dom/CacheExpirationTime.h"
17 #include "mozilla/dom/SRIMetadata.h"
18 #include "mozilla/LinkedList.h"
19 #include "mozilla/Maybe.h"
20 #include "mozilla/PreloaderBase.h"
21 #include "mozilla/RefPtr.h"
22 #include "mozilla/SharedSubResourceCache.h" // mozilla::SubResourceNetworkMetadataHolder
23 #include "mozilla/StaticPrefs_dom.h"
24 #include "mozilla/Variant.h"
25 #include "mozilla/Vector.h"
26 #include "nsCycleCollectionParticipant.h"
27 #include "nsIGlobalObject.h"
28 #include "LoadedScript.h"
29 #include "ScriptKind.h"
30 #include "ScriptFetchOptions.h"
32 class nsICacheInfoChannel
;
34 namespace mozilla::dom
{
36 class ScriptLoadContext
;
37 class WorkerLoadContext
;
38 class WorkletLoadContext
;
39 enum class RequestPriority
: uint8_t;
41 } // namespace mozilla::dom
43 namespace mozilla::loader
{
44 class SyncLoadContext
;
45 } // namespace mozilla::loader
50 class LoadContextBase
;
51 class ModuleLoadRequest
;
52 class ScriptLoadRequestList
;
57 * ScriptLoadRequest is a generic representation of a JavaScript script that
58 * will be loaded by a Script/Module loader. This representation is used by the
59 * DOM ScriptLoader and will be used by workers and MOZJSComponentLoader.
61 * The ScriptLoadRequest contains information about the kind of script (classic
62 * or module), the URI, and the ScriptFetchOptions associated with the script.
63 * It is responsible for holding the script data once the fetch is complete, or
64 * if the request is cached, the bytecode.
66 * Relationship to ScriptLoadContext:
68 * ScriptLoadRequest and ScriptLoadContexts have a circular pointer. A
69 * ScriptLoadContext augments the loading of a ScriptLoadRequest by providing
70 * additional information regarding the loading and evaluation behavior (see
71 * the ScriptLoadContext class for details). In terms of responsibility,
72 * the ScriptLoadRequest represents "What" is being loaded, and the
73 * ScriptLoadContext represents "How".
75 * TODO: see if we can use it in the jsshell script loader. We need to either
76 * remove ISUPPORTS or find a way to encorporate that in the jsshell. We would
77 * then only have one implementation of the script loader, and it would be
78 * tested whenever jsshell tests are run. This would mean finding another way to
79 * create ScriptLoadRequest lists.
83 class ScriptLoadRequest
: public nsISupports
,
84 private mozilla::LinkedListElement
<ScriptLoadRequest
>,
85 public LoadedScriptDelegate
<ScriptLoadRequest
> {
86 using super
= LinkedListElement
<ScriptLoadRequest
>;
88 // Allow LinkedListElement<ScriptLoadRequest> to cast us to itself as needed.
89 friend class mozilla::LinkedListElement
<ScriptLoadRequest
>;
90 friend class ScriptLoadRequestList
;
93 virtual ~ScriptLoadRequest();
96 using SRIMetadata
= mozilla::dom::SRIMetadata
;
97 ScriptLoadRequest(ScriptKind aKind
, nsIURI
* aURI
,
98 mozilla::dom::ReferrerPolicy aReferrerPolicy
,
99 ScriptFetchOptions
* aFetchOptions
,
100 const SRIMetadata
& aIntegrity
, nsIURI
* aReferrer
,
101 LoadContextBase
* aContext
);
103 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
104 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadRequest
)
106 using super::getNext
;
107 using super::isInList
;
109 template <typename T
, typename D
= JS::DeletePolicy
<T
>>
110 using UniquePtr
= mozilla::UniquePtr
<T
, D
>;
112 bool IsModuleRequest() const { return mKind
== ScriptKind::eModule
; }
113 bool IsImportMapRequest() const { return mKind
== ScriptKind::eImportMap
; }
115 ModuleLoadRequest
* AsModuleRequest();
116 const ModuleLoadRequest
* AsModuleRequest() const;
118 bool IsCacheable() const;
120 CacheExpirationTime
ExpirationTime() const { return mExpirationTime
; }
122 void SetMinimumExpirationTime(const CacheExpirationTime
& aExpirationTime
) {
123 mExpirationTime
.SetMinimum(aExpirationTime
);
126 virtual bool IsTopLevel() const { return true; };
128 virtual void Cancel();
130 virtual void SetReady();
132 enum class State
: uint8_t {
134 PendingFetchingError
,
143 // Before any attempt at fetching resources from the cache we should first
144 // make sure that the resource does not yet exists in the cache. In which case
145 // we might simply alias its LoadedScript. Otherwise a new one would be
147 bool IsCheckingCache() const { return mState
== State::CheckingCache
; }
149 // Setup and load resources, to fill the LoadedScript and make it usable by
150 // the JavaScript engine.
151 bool IsFetching() const { return mState
== State::Fetching
; }
152 bool IsCompiling() const { return mState
== State::Compiling
; }
153 bool IsLoadingImports() const { return mState
== State::LoadingImports
; }
154 bool IsCancelingImports() const { return mState
== State::CancelingImports
; }
155 bool IsCanceled() const { return mState
== State::Canceled
; }
157 bool IsPendingFetchingError() const {
158 return mState
== State::PendingFetchingError
;
161 // Return whether the request has been completed, either successfully or
163 bool IsFinished() const {
164 return mState
== State::Ready
|| mState
== State::Canceled
;
167 mozilla::dom::RequestPriority
FetchPriority() const {
168 return mFetchOptions
->mFetchPriority
;
171 enum mozilla::dom::ReferrerPolicy
ReferrerPolicy() const {
172 return mReferrerPolicy
;
175 void UpdateReferrerPolicy(mozilla::dom::ReferrerPolicy aReferrerPolicy
) {
176 mReferrerPolicy
= aReferrerPolicy
;
179 enum ParserMetadata
ParserMetadata() const {
180 return mFetchOptions
->mParserMetadata
;
183 const nsString
& Nonce() const { return mFetchOptions
->mNonce
; }
185 nsIPrincipal
* TriggeringPrincipal() const {
186 return mFetchOptions
->mTriggeringPrincipal
;
189 // Convert a CheckingCache ScriptLoadRequest into a Ready one, by populating
190 // the script data from cached script.
191 void CacheEntryFound(LoadedScript
* aLoadedScript
);
193 // Convert a CheckingCache ScriptLoadRequest into a Fetching one, by creating
194 // a new LoadedScript which is matching the ScriptKind provided when
195 // constructing this ScriptLoadRequest.
196 void NoCacheEntryFound();
198 void SetPendingFetchingError();
200 bool PassedConditionForBytecodeEncoding() const {
201 return mBytecodeEncodingPlan
== BytecodeEncodingPlan::PassedCondition
||
202 mBytecodeEncodingPlan
== BytecodeEncodingPlan::MarkedForEncode
;
205 void MarkSkippedBytecodeEncoding() {
206 MOZ_ASSERT(mBytecodeEncodingPlan
== BytecodeEncodingPlan::Uninitialized
||
207 mBytecodeEncodingPlan
== BytecodeEncodingPlan::PassedCondition
);
208 mBytecodeEncodingPlan
= BytecodeEncodingPlan::Skipped
;
211 void MarkPassedConditionForBytecodeEncoding() {
212 MOZ_ASSERT(mBytecodeEncodingPlan
== BytecodeEncodingPlan::Uninitialized
);
213 mBytecodeEncodingPlan
= BytecodeEncodingPlan::PassedCondition
;
216 bool IsMarkedForBytecodeEncoding() const {
217 return mBytecodeEncodingPlan
== BytecodeEncodingPlan::MarkedForEncode
;
221 void MarkForBytecodeEncoding() {
222 MOZ_ASSERT(mBytecodeEncodingPlan
== BytecodeEncodingPlan::PassedCondition
);
223 mBytecodeEncodingPlan
= BytecodeEncodingPlan::MarkedForEncode
;
227 void MarkScriptForBytecodeEncoding(JSScript
* aScript
);
229 mozilla::CORSMode
CORSMode() const { return mFetchOptions
->mCORSMode
; }
231 void DropBytecodeCacheReferences();
233 bool HasLoadContext() const { return mLoadContext
; }
234 bool HasScriptLoadContext() const;
235 bool HasWorkerLoadContext() const;
237 mozilla::dom::ScriptLoadContext
* GetScriptLoadContext();
238 const mozilla::dom::ScriptLoadContext
* GetScriptLoadContext() const;
240 mozilla::loader::SyncLoadContext
* GetSyncLoadContext();
242 mozilla::dom::WorkerLoadContext
* GetWorkerLoadContext();
244 mozilla::dom::WorkletLoadContext
* GetWorkletLoadContext();
246 const LoadedScript
* getLoadedScript() const { return mLoadedScript
.get(); }
247 LoadedScript
* getLoadedScript() { return mLoadedScript
.get(); }
250 * Set the request's mBaseURL, based on aChannel.
251 * aOriginalURI is the result of aChannel->GetOriginalURI.
253 void SetBaseURLFromChannelAndOriginalURI(nsIChannel
* aChannel
,
254 nsIURI
* aOriginalURI
);
256 const ScriptKind mKind
; // Whether this is a classic script or a module
259 State mState
; // Are we still waiting for a load to complete?
260 bool mFetchSourceOnly
; // Request source, not cached bytecode.
262 enum class BytecodeEncodingPlan
: uint8_t {
263 // This is not yet considered for encoding.
266 // This is marked for skipping the encoding.
269 // This fits the condition for the encoding (e.g. file size, fetch count).
272 // This is marked for encoding, with setting sufficient input,
273 // e.g. mScriptForBytecodeEncoding for script.
276 BytecodeEncodingPlan mBytecodeEncodingPlan
=
277 BytecodeEncodingPlan::Uninitialized
;
279 // The referrer policy used for the initial fetch and for fetching any
281 enum mozilla::dom::ReferrerPolicy mReferrerPolicy
;
283 CacheExpirationTime mExpirationTime
= CacheExpirationTime::Never();
285 RefPtr
<ScriptFetchOptions
> mFetchOptions
;
286 RefPtr
<mozilla::SubResourceNetworkMetadataHolder
> mNetworkMetadata
;
287 const SRIMetadata mIntegrity
;
288 const nsCOMPtr
<nsIURI
> mReferrer
;
289 mozilla::Maybe
<nsString
>
290 mSourceMapURL
; // Holds source map url for loaded scripts
292 const nsCOMPtr
<nsIURI
> mURI
;
293 nsCOMPtr
<nsIPrincipal
> mOriginPrincipal
;
295 // Keep the URI's filename alive during off thread parsing.
296 // Also used by workers to report on errors while loading, and used by
297 // worklets as the file name in compile options.
300 // The base URL used for resolving relative module imports.
301 nsCOMPtr
<nsIURI
> mBaseURL
;
303 // The loaded script holds the source / bytecode which is loaded.
305 // Currently it is used to hold information which are needed by the Debugger.
306 // Soon it would be used as a way to dissociate the LoadRequest from the
307 // loaded value, such that multiple request referring to the same content
308 // would share the same loaded script.
309 RefPtr
<LoadedScript
> mLoadedScript
;
311 // Holds the top-level JSScript that corresponds to the current source, once
312 // it is parsed, and marked to be saved in the bytecode cache.
314 // NOTE: This field is not used for ModuleLoadRequest.
315 JS::Heap
<JSScript
*> mScriptForBytecodeEncoding
;
317 // Holds the Cache information, which is used to register the bytecode
318 // on the cache entry, such that we can load it the next time.
319 nsCOMPtr
<nsICacheInfoChannel
> mCacheInfo
;
321 // LoadContext for augmenting the load depending on the loading
322 // context (DOM, Worker, etc.)
323 RefPtr
<LoadContextBase
> mLoadContext
;
325 // EarlyHintRegistrar id to connect the http channel back to the preload, with
326 // a default of value of 0 indicating that this request is not an early hints
328 uint64_t mEarlyHintPreloaderId
;
331 class ScriptLoadRequestList
: private mozilla::LinkedList
<ScriptLoadRequest
> {
332 using super
= mozilla::LinkedList
<ScriptLoadRequest
>;
335 ~ScriptLoadRequestList();
337 void CancelRequestsAndClear();
340 bool Contains(ScriptLoadRequest
* aElem
) const;
343 using super::getFirst
;
344 using super::isEmpty
;
346 void AppendElement(ScriptLoadRequest
* aElem
) {
347 MOZ_ASSERT(!aElem
->isInList());
352 already_AddRefed
<ScriptLoadRequest
> Steal(ScriptLoadRequest
* aElem
) {
353 aElem
->removeFrom(*this);
354 return dont_AddRef(aElem
);
357 already_AddRefed
<ScriptLoadRequest
> StealFirst() {
358 MOZ_ASSERT(!isEmpty());
359 return Steal(getFirst());
362 void Remove(ScriptLoadRequest
* aElem
) {
363 aElem
->removeFrom(*this);
368 inline void ImplCycleCollectionUnlink(ScriptLoadRequestList
& aField
) {
369 while (!aField
.isEmpty()) {
370 RefPtr
<ScriptLoadRequest
> first
= aField
.StealFirst();
374 inline void ImplCycleCollectionTraverse(
375 nsCycleCollectionTraversalCallback
& aCallback
,
376 ScriptLoadRequestList
& aField
, const char* aName
, uint32_t aFlags
) {
377 for (ScriptLoadRequest
* request
= aField
.getFirst(); request
;
378 request
= request
->getNext()) {
379 CycleCollectionNoteChild(aCallback
, request
, aName
, aFlags
);
383 } // namespace loader
386 #endif // js_loader_ScriptLoadRequest_h