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/AllocPolicy.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/CORSMode.h"
17 #include "mozilla/dom/SRIMetadata.h"
18 #include "mozilla/dom/ReferrerPolicyBinding.h"
19 #include "mozilla/LinkedList.h"
20 #include "mozilla/Maybe.h"
21 #include "mozilla/MaybeOneOf.h"
22 #include "mozilla/PreloaderBase.h"
23 #include "mozilla/StaticPrefs_dom.h"
24 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
25 #include "mozilla/Variant.h"
26 #include "mozilla/Vector.h"
28 #include "nsCycleCollectionParticipant.h"
29 #include "nsIGlobalObject.h"
30 #include "ScriptKind.h"
31 #include "nsIScriptElement.h"
33 class nsICacheInfoChannel
;
35 namespace mozilla::dom
{
37 class ScriptLoadContext
;
38 class WorkerLoadContext
;
39 class WorkletLoadContext
;
40 enum class RequestPriority
: uint8_t;
42 } // namespace mozilla::dom
44 namespace mozilla::loader
{
45 class ComponentLoadContext
;
46 } // namespace mozilla::loader
51 using Utf8Unit
= mozilla::Utf8Unit
;
53 class LoadContextBase
;
54 class ModuleLoadRequest
;
55 class ScriptLoadRequestList
;
57 // https://fetch.spec.whatwg.org/#concept-request-parser-metadata
58 // All scripts are either "parser-inserted" or "not-parser-inserted", so
59 // the empty string is not necessary.
60 enum class ParserMetadata
{
66 * ScriptFetchOptions loosely corresponds to HTML's "script fetch options",
67 * https://html.spec.whatwg.org/multipage/webappapis.html#script-fetch-options
68 * with the exception of the following properties:
70 * The integrity metadata used for the initial fetch. This is
71 * implemented in ScriptLoadRequest, as it changes for every
75 * For a module script, its referrerPolicy will be updated if there is a
76 * HTTP Response 'REFERRER-POLICY' header, given this value may be different
77 * for every ScriptLoadRequest, so we store it directly in
80 * In the case of classic scripts without dynamic import, this object is
81 * used once. For modules, this object is propogated throughout the module
82 * tree. If there is a dynamically imported module in any type of script,
83 * the ScriptFetchOptions object will be propogated from its importer.
86 class ScriptFetchOptions
{
87 ~ScriptFetchOptions();
90 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ScriptFetchOptions
)
91 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ScriptFetchOptions
)
93 ScriptFetchOptions(mozilla::CORSMode aCORSMode
, const nsAString
& aNonce
,
94 mozilla::dom::RequestPriority aFetchPriority
,
95 const ParserMetadata aParserMetadata
,
96 nsIPrincipal
* aTriggeringPrincipal
,
97 mozilla::dom::Element
* aElement
= nullptr);
100 * The credentials mode used for the initial fetch (for module scripts)
101 * and for fetching any imported modules (for both module scripts and
104 const mozilla::CORSMode mCORSMode
;
107 * The cryptographic nonce metadata used for the initial fetch and for
108 * fetching any imported modules.
110 const nsString mNonce
;
113 * <https://html.spec.whatwg.org/multipage/webappapis.html#script-fetch-options>.
115 const mozilla::dom::RequestPriority mFetchPriority
;
118 * The parser metadata used for the initial fetch and for fetching any
121 const ParserMetadata mParserMetadata
;
124 * Used to determine CSP and if we are on the About page.
125 * Only used in DOM content scripts.
126 * TODO: Move to ScriptLoadContext
128 nsCOMPtr
<nsIPrincipal
> mTriggeringPrincipal
;
130 * Represents fields populated by DOM elements (nonce, parser metadata)
131 * Leave this field as a nullptr for any fetch that requires the
132 * default classic script options.
133 * (https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options)
134 * TODO: extract necessary fields rather than passing this object
136 nsCOMPtr
<mozilla::dom::Element
> mElement
;
142 * ScriptLoadRequest is a generic representation of a JavaScript script that
143 * will be loaded by a Script/Module loader. This representation is used by the
144 * DOM ScriptLoader and will be used by workers and MOZJSComponentLoader.
146 * The ScriptLoadRequest contains information about the kind of script (classic
147 * or module), the URI, and the ScriptFetchOptions associated with the script.
148 * It is responsible for holding the script data once the fetch is complete, or
149 * if the request is cached, the bytecode.
151 * Relationship to ScriptLoadContext:
153 * ScriptLoadRequest and ScriptLoadContexts have a circular pointer. A
154 * ScriptLoadContext augments the loading of a ScriptLoadRequest by providing
155 * additional information regarding the loading and evaluation behavior (see
156 * the ScriptLoadContext class for details). In terms of responsibility,
157 * the ScriptLoadRequest represents "What" is being loaded, and the
158 * ScriptLoadContext represents "How".
160 * TODO: see if we can use it in the jsshell script loader. We need to either
161 * remove ISUPPORTS or find a way to encorporate that in the jsshell. We would
162 * then only have one implementation of the script loader, and it would be
163 * tested whenever jsshell tests are run. This would mean finding another way to
164 * create ScriptLoadRequest lists.
168 class ScriptLoadRequest
169 : public nsISupports
,
170 private mozilla::LinkedListElement
<ScriptLoadRequest
> {
171 using super
= LinkedListElement
<ScriptLoadRequest
>;
173 // Allow LinkedListElement<ScriptLoadRequest> to cast us to itself as needed.
174 friend class mozilla::LinkedListElement
<ScriptLoadRequest
>;
175 friend class ScriptLoadRequestList
;
178 virtual ~ScriptLoadRequest();
181 using SRIMetadata
= mozilla::dom::SRIMetadata
;
182 ScriptLoadRequest(ScriptKind aKind
, nsIURI
* aURI
,
183 mozilla::dom::ReferrerPolicy aReferrerPolicy
,
184 ScriptFetchOptions
* aFetchOptions
,
185 const SRIMetadata
& aIntegrity
, nsIURI
* aReferrer
,
186 LoadContextBase
* aContext
);
188 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
189 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadRequest
)
191 using super::getNext
;
192 using super::isInList
;
194 template <typename T
>
195 using VariantType
= mozilla::VariantType
<T
>;
197 template <typename
... Ts
>
198 using Variant
= mozilla::Variant
<Ts
...>;
200 template <typename T
, typename D
= JS::DeletePolicy
<T
>>
201 using UniquePtr
= mozilla::UniquePtr
<T
, D
>;
203 using MaybeSourceText
=
204 mozilla::MaybeOneOf
<JS::SourceText
<char16_t
>, JS::SourceText
<Utf8Unit
>>;
206 bool IsModuleRequest() const { return mKind
== ScriptKind::eModule
; }
207 bool IsImportMapRequest() const { return mKind
== ScriptKind::eImportMap
; }
209 ModuleLoadRequest
* AsModuleRequest();
210 const ModuleLoadRequest
* AsModuleRequest() const;
212 virtual bool IsTopLevel() const { return true; };
214 virtual void Cancel();
216 virtual void SetReady();
218 enum class State
: uint8_t {
226 bool IsFetching() const { return mState
== State::Fetching
; }
227 bool IsCompiling() const { return mState
== State::Compiling
; }
228 bool IsLoadingImports() const { return mState
== State::LoadingImports
; }
229 bool IsCanceled() const { return mState
== State::Canceled
; }
231 // Return whether the request has been completed, either successfully or
233 bool IsFinished() const {
234 return mState
== State::Ready
|| mState
== State::Canceled
;
237 // Type of data provided by the nsChannel.
238 enum class DataType
: uint8_t { eUnknown
, eTextSource
, eBytecode
};
240 bool IsUnknownDataType() const { return mDataType
== DataType::eUnknown
; }
241 bool IsTextSource() const { return mDataType
== DataType::eTextSource
; }
242 bool IsSource() const { return IsTextSource(); }
244 void SetUnknownDataType() {
245 mDataType
= DataType::eUnknown
;
249 void SetTextSource() {
250 MOZ_ASSERT(IsUnknownDataType());
251 mDataType
= DataType::eTextSource
;
252 mScriptData
.emplace(VariantType
<ScriptTextBuffer
<Utf8Unit
>>());
255 // Use a vector backed by the JS allocator for script text so that contents
256 // can be transferred in constant time to the JS engine, not copied in linear
258 template <typename Unit
>
259 using ScriptTextBuffer
= mozilla::Vector
<Unit
, 0, js::MallocAllocPolicy
>;
261 bool IsUTF16Text() const {
262 return mScriptData
->is
<ScriptTextBuffer
<char16_t
>>();
264 bool IsUTF8Text() const {
265 return mScriptData
->is
<ScriptTextBuffer
<Utf8Unit
>>();
268 template <typename Unit
>
269 const ScriptTextBuffer
<Unit
>& ScriptText() const {
270 MOZ_ASSERT(IsTextSource());
271 return mScriptData
->as
<ScriptTextBuffer
<Unit
>>();
273 template <typename Unit
>
274 ScriptTextBuffer
<Unit
>& ScriptText() {
275 MOZ_ASSERT(IsTextSource());
276 return mScriptData
->as
<ScriptTextBuffer
<Unit
>>();
279 size_t ScriptTextLength() const {
280 MOZ_ASSERT(IsTextSource());
281 return IsUTF16Text() ? ScriptText
<char16_t
>().length()
282 : ScriptText
<Utf8Unit
>().length();
285 // Get source text. On success |aMaybeSource| will contain either UTF-8 or
286 // UTF-16 source; on failure it will remain in its initial state.
287 nsresult
GetScriptSource(JSContext
* aCx
, MaybeSourceText
* aMaybeSource
);
289 void ClearScriptText() {
290 MOZ_ASSERT(IsTextSource());
291 return IsUTF16Text() ? ScriptText
<char16_t
>().clearAndFree()
292 : ScriptText
<Utf8Unit
>().clearAndFree();
295 mozilla::dom::RequestPriority
FetchPriority() const {
296 return mFetchOptions
->mFetchPriority
;
299 enum mozilla::dom::ReferrerPolicy
ReferrerPolicy() const {
300 return mReferrerPolicy
;
303 void UpdateReferrerPolicy(mozilla::dom::ReferrerPolicy aReferrerPolicy
) {
304 mReferrerPolicy
= aReferrerPolicy
;
307 enum ParserMetadata
ParserMetadata() const {
308 return mFetchOptions
->mParserMetadata
;
311 const nsString
& Nonce() const { return mFetchOptions
->mNonce
; }
313 nsIPrincipal
* TriggeringPrincipal() const {
314 return mFetchOptions
->mTriggeringPrincipal
;
317 void ClearScriptSource();
319 void MarkForBytecodeEncoding(JSScript
* aScript
);
321 bool IsMarkedForBytecodeEncoding() const;
323 bool IsBytecode() const { return mDataType
== DataType::eBytecode
; }
327 mozilla::CORSMode
CORSMode() const { return mFetchOptions
->mCORSMode
; }
329 void DropBytecodeCacheReferences();
331 bool HasLoadContext() const { return mLoadContext
; }
332 bool HasScriptLoadContext() const;
333 bool HasWorkerLoadContext() const;
335 mozilla::dom::ScriptLoadContext
* GetScriptLoadContext();
337 mozilla::loader::ComponentLoadContext
* GetComponentLoadContext();
339 mozilla::dom::WorkerLoadContext
* GetWorkerLoadContext();
341 mozilla::dom::WorkletLoadContext
* GetWorkletLoadContext();
343 const ScriptKind mKind
; // Whether this is a classic script or a module
346 State mState
; // Are we still waiting for a load to complete?
347 bool mFetchSourceOnly
; // Request source, not cached bytecode.
348 DataType mDataType
; // Does this contain Source or Bytecode?
350 // The referrer policy used for the initial fetch and for fetching any
352 enum mozilla::dom::ReferrerPolicy mReferrerPolicy
;
353 RefPtr
<ScriptFetchOptions
> mFetchOptions
;
354 const SRIMetadata mIntegrity
;
355 const nsCOMPtr
<nsIURI
> mReferrer
;
356 mozilla::Maybe
<nsString
>
357 mSourceMapURL
; // Holds source map url for loaded scripts
359 // Holds script source data for non-inline scripts.
361 Variant
<ScriptTextBuffer
<char16_t
>, ScriptTextBuffer
<Utf8Unit
>>>
364 // The length of script source text, set when reading completes. This is used
365 // since mScriptData is cleared when the source is passed to the JS engine.
366 size_t mScriptTextLength
;
368 // Holds the SRI serialized hash and the script bytecode for non-inline
369 // scripts. The data is laid out according to ScriptBytecodeDataLayout
370 // or, if compression is enabled, ScriptBytecodeCompressedDataLayout.
371 mozilla::Vector
<uint8_t> mScriptBytecode
;
372 uint32_t mBytecodeOffset
; // Offset of the bytecode in mScriptBytecode
374 const nsCOMPtr
<nsIURI
> mURI
;
375 nsCOMPtr
<nsIPrincipal
> mOriginPrincipal
;
377 // Keep the URI's filename alive during off thread parsing.
378 // Also used by workers to report on errors while loading, and used by
379 // worklets as the file name in compile options.
382 // The base URL used for resolving relative module imports.
383 nsCOMPtr
<nsIURI
> mBaseURL
;
385 // Holds the top-level JSScript that corresponds to the current source, once
386 // it is parsed, and planned to be saved in the bytecode cache.
388 // NOTE: This field is not used for ModuleLoadRequest.
389 // See ModuleLoadRequest::mIsMarkedForBytecodeEncoding.
390 JS::Heap
<JSScript
*> mScriptForBytecodeEncoding
;
392 // Holds the Cache information, which is used to register the bytecode
393 // on the cache entry, such that we can load it the next time.
394 nsCOMPtr
<nsICacheInfoChannel
> mCacheInfo
;
396 // LoadContext for augmenting the load depending on the loading
397 // context (DOM, Worker, etc.)
398 RefPtr
<LoadContextBase
> mLoadContext
;
400 // EarlyHintRegistrar id to connect the http channel back to the preload, with
401 // a default of value of 0 indicating that this request is not an early hints
403 uint64_t mEarlyHintPreloaderId
;
406 class ScriptLoadRequestList
: private mozilla::LinkedList
<ScriptLoadRequest
> {
407 using super
= mozilla::LinkedList
<ScriptLoadRequest
>;
410 ~ScriptLoadRequestList();
412 void CancelRequestsAndClear();
415 bool Contains(ScriptLoadRequest
* aElem
) const;
418 using super::getFirst
;
419 using super::isEmpty
;
421 void AppendElement(ScriptLoadRequest
* aElem
) {
422 MOZ_ASSERT(!aElem
->isInList());
427 already_AddRefed
<ScriptLoadRequest
> Steal(ScriptLoadRequest
* aElem
) {
428 aElem
->removeFrom(*this);
429 return dont_AddRef(aElem
);
432 already_AddRefed
<ScriptLoadRequest
> StealFirst() {
433 MOZ_ASSERT(!isEmpty());
434 return Steal(getFirst());
437 void Remove(ScriptLoadRequest
* aElem
) {
438 aElem
->removeFrom(*this);
443 inline void ImplCycleCollectionUnlink(ScriptLoadRequestList
& aField
) {
444 while (!aField
.isEmpty()) {
445 RefPtr
<ScriptLoadRequest
> first
= aField
.StealFirst();
449 inline void ImplCycleCollectionTraverse(
450 nsCycleCollectionTraversalCallback
& aCallback
,
451 ScriptLoadRequestList
& aField
, const char* aName
, uint32_t aFlags
) {
452 for (ScriptLoadRequest
* request
= aField
.getFirst(); request
;
453 request
= request
->getNext()) {
454 CycleCollectionNoteChild(aCallback
, request
, aName
, aFlags
);
458 } // namespace loader
461 #endif // js_loader_ScriptLoadRequest_h