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
;
41 } // namespace mozilla::dom
43 namespace mozilla::loader
{
44 class ComponentLoadContext
;
45 } // namespace mozilla::loader
52 using Utf8Unit
= mozilla::Utf8Unit
;
54 class LoadContextBase
;
55 class ModuleLoadRequest
;
56 class ScriptLoadRequestList
;
59 * ScriptFetchOptions loosely corresponds to HTML's "script fetch options",
60 * https://html.spec.whatwg.org/multipage/webappapis.html#script-fetch-options
61 * with the exception of the following properties:
63 * The cryptographic nonce metadata used for the initial fetch and for
64 * fetching any imported modules. As this is populated by a DOM element,
65 * this is implemented via mozilla::dom::Element as the field
66 * mElement. The default value is an empty string, and is indicated
67 * when this field is a nullptr. Nonce is not represented on the dom
68 * side as per bug 1374612.
70 * The parser metadata used for the initial fetch and for fetching any
71 * imported modules. This is populated from a mozilla::dom::Element and is
72 * handled by the field mElement. The default value is an empty string,
73 * and is indicated when this field is a nullptr.
75 * The integrity metadata used for the initial fetch. This is
76 * implemented in ScriptLoadRequest, as it changes for every
79 * In the case of classic scripts without dynamic import, this object is
80 * used once. For modules, this object is propogated throughout the module
81 * tree. If there is a dynamically imported module in any type of script,
82 * the ScriptFetchOptions object will be propogated from its importer.
85 class ScriptFetchOptions
{
86 ~ScriptFetchOptions();
89 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ScriptFetchOptions
)
90 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ScriptFetchOptions
)
92 ScriptFetchOptions(mozilla::CORSMode aCORSMode
,
93 enum mozilla::dom::ReferrerPolicy aReferrerPolicy
,
94 nsIPrincipal
* aTriggeringPrincipal
,
95 mozilla::dom::Element
* aElement
= nullptr);
98 * The credentials mode used for the initial fetch (for module scripts)
99 * and for fetching any imported modules (for both module scripts and
102 const mozilla::CORSMode mCORSMode
;
105 * The referrer policy used for the initial fetch and for fetching any
108 const enum mozilla::dom::ReferrerPolicy mReferrerPolicy
;
111 * Used to determine CSP and if we are on the About page.
112 * Only used in DOM content scripts.
113 * TODO: Move to ScriptLoadContext
115 nsCOMPtr
<nsIPrincipal
> mTriggeringPrincipal
;
117 * Represents fields populated by DOM elements (nonce, parser metadata)
118 * Leave this field as a nullptr for any fetch that requires the
119 * default classic script options.
120 * (https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options)
121 * TODO: extract necessary fields rather than passing this object
123 nsCOMPtr
<mozilla::dom::Element
> mElement
;
129 * ScriptLoadRequest is a generic representation of a JavaScript script that
130 * will be loaded by a Script/Module loader. This representation is used by the
131 * DOM ScriptLoader and will be used by workers and MOZJSComponentLoader.
133 * The ScriptLoadRequest contains information about the kind of script (classic
134 * or module), the URI, and the ScriptFetchOptions associated with the script.
135 * It is responsible for holding the script data once the fetch is complete, or
136 * if the request is cached, the bytecode.
138 * Relationship to ScriptLoadContext:
140 * ScriptLoadRequest and ScriptLoadContexts have a circular pointer. A
141 * ScriptLoadContext augments the loading of a ScriptLoadRequest by providing
142 * additional information regarding the loading and evaluation behavior (see
143 * the ScriptLoadContext class for details). In terms of responsibility,
144 * the ScriptLoadRequest represents "What" is being loaded, and the
145 * ScriptLoadContext represents "How".
147 * TODO: see if we can use it in the jsshell script loader. We need to either
148 * remove ISUPPORTS or find a way to encorporate that in the jsshell. We would
149 * then only have one implementation of the script loader, and it would be
150 * tested whenever jsshell tests are run. This would mean finding another way to
151 * create ScriptLoadRequest lists.
155 class ScriptLoadRequest
156 : public nsISupports
,
157 private mozilla::LinkedListElement
<ScriptLoadRequest
> {
158 using super
= LinkedListElement
<ScriptLoadRequest
>;
160 // Allow LinkedListElement<ScriptLoadRequest> to cast us to itself as needed.
161 friend class mozilla::LinkedListElement
<ScriptLoadRequest
>;
162 friend class ScriptLoadRequestList
;
165 virtual ~ScriptLoadRequest();
168 using SRIMetadata
= mozilla::dom::SRIMetadata
;
169 ScriptLoadRequest(ScriptKind aKind
, nsIURI
* aURI
,
170 ScriptFetchOptions
* aFetchOptions
,
171 const SRIMetadata
& aIntegrity
, nsIURI
* aReferrer
,
172 LoadContextBase
* aContext
);
174 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
175 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadRequest
)
177 using super::getNext
;
178 using super::isInList
;
180 template <typename T
>
181 using VariantType
= mozilla::VariantType
<T
>;
183 template <typename
... Ts
>
184 using Variant
= mozilla::Variant
<Ts
...>;
186 template <typename T
, typename D
= JS::DeletePolicy
<T
>>
187 using UniquePtr
= mozilla::UniquePtr
<T
, D
>;
189 using MaybeSourceText
=
190 mozilla::MaybeOneOf
<JS::SourceText
<char16_t
>, JS::SourceText
<Utf8Unit
>>;
192 bool IsModuleRequest() const { return mKind
== ScriptKind::eModule
; }
193 bool IsImportMapRequest() const { return mKind
== ScriptKind::eImportMap
; }
195 ModuleLoadRequest
* AsModuleRequest();
196 const ModuleLoadRequest
* AsModuleRequest() const;
198 virtual bool IsTopLevel() const { return true; };
200 virtual void Cancel();
202 virtual void SetReady();
204 enum class State
: uint8_t {
212 bool IsFetching() const { return mState
== State::Fetching
; }
213 bool IsCompiling() const { return mState
== State::Compiling
; }
214 bool IsLoadingImports() const { return mState
== State::LoadingImports
; }
216 bool IsReadyToRun() const {
217 return mState
== State::Ready
|| mState
== State::Canceled
;
220 bool IsCanceled() const { return mState
== State::Canceled
; }
222 // Type of data provided by the nsChannel.
223 enum class DataType
: uint8_t { eUnknown
, eTextSource
, eBytecode
};
225 bool IsUnknownDataType() const { return mDataType
== DataType::eUnknown
; }
226 bool IsTextSource() const { return mDataType
== DataType::eTextSource
; }
227 bool IsSource() const { return IsTextSource(); }
229 void SetUnknownDataType() {
230 mDataType
= DataType::eUnknown
;
234 bool IsUTF8ParsingEnabled();
236 void SetTextSource() {
237 MOZ_ASSERT(IsUnknownDataType());
238 mDataType
= DataType::eTextSource
;
239 if (IsUTF8ParsingEnabled()) {
240 mScriptData
.emplace(VariantType
<ScriptTextBuffer
<Utf8Unit
>>());
242 mScriptData
.emplace(VariantType
<ScriptTextBuffer
<char16_t
>>());
246 // Use a vector backed by the JS allocator for script text so that contents
247 // can be transferred in constant time to the JS engine, not copied in linear
249 template <typename Unit
>
250 using ScriptTextBuffer
= mozilla::Vector
<Unit
, 0, js::MallocAllocPolicy
>;
252 bool IsUTF16Text() const {
253 return mScriptData
->is
<ScriptTextBuffer
<char16_t
>>();
255 bool IsUTF8Text() const {
256 return mScriptData
->is
<ScriptTextBuffer
<Utf8Unit
>>();
259 template <typename Unit
>
260 const ScriptTextBuffer
<Unit
>& ScriptText() const {
261 MOZ_ASSERT(IsTextSource());
262 return mScriptData
->as
<ScriptTextBuffer
<Unit
>>();
264 template <typename Unit
>
265 ScriptTextBuffer
<Unit
>& ScriptText() {
266 MOZ_ASSERT(IsTextSource());
267 return mScriptData
->as
<ScriptTextBuffer
<Unit
>>();
270 size_t ScriptTextLength() const {
271 MOZ_ASSERT(IsTextSource());
272 return IsUTF16Text() ? ScriptText
<char16_t
>().length()
273 : ScriptText
<Utf8Unit
>().length();
276 // Get source text. On success |aMaybeSource| will contain either UTF-8 or
277 // UTF-16 source; on failure it will remain in its initial state.
278 nsresult
GetScriptSource(JSContext
* aCx
, MaybeSourceText
* aMaybeSource
);
280 void ClearScriptText() {
281 MOZ_ASSERT(IsTextSource());
282 return IsUTF16Text() ? ScriptText
<char16_t
>().clearAndFree()
283 : ScriptText
<Utf8Unit
>().clearAndFree();
286 enum mozilla::dom::ReferrerPolicy
ReferrerPolicy() const {
287 return mFetchOptions
->mReferrerPolicy
;
290 nsIPrincipal
* TriggeringPrincipal() const {
291 return mFetchOptions
->mTriggeringPrincipal
;
294 void ClearScriptSource();
296 void MarkForBytecodeEncoding(JSScript
* aScript
);
298 bool IsMarkedForBytecodeEncoding() const;
300 bool IsBytecode() const { return mDataType
== DataType::eBytecode
; }
304 mozilla::CORSMode
CORSMode() const { return mFetchOptions
->mCORSMode
; }
306 void DropBytecodeCacheReferences();
308 bool HasLoadContext() const { return mLoadContext
; }
309 bool HasScriptLoadContext() const;
310 bool HasWorkerLoadContext() const;
312 mozilla::dom::ScriptLoadContext
* GetScriptLoadContext();
314 mozilla::loader::ComponentLoadContext
* GetComponentLoadContext();
316 mozilla::dom::WorkerLoadContext
* GetWorkerLoadContext();
318 mozilla::dom::WorkletLoadContext
* GetWorkletLoadContext();
320 const ScriptKind mKind
; // Whether this is a classic script or a module
323 State mState
; // Are we still waiting for a load to complete?
324 bool mFetchSourceOnly
; // Request source, not cached bytecode.
325 DataType mDataType
; // Does this contain Source or Bytecode?
326 RefPtr
<ScriptFetchOptions
> mFetchOptions
;
327 const SRIMetadata mIntegrity
;
328 const nsCOMPtr
<nsIURI
> mReferrer
;
329 mozilla::Maybe
<nsString
>
330 mSourceMapURL
; // Holds source map url for loaded scripts
332 // Holds script source data for non-inline scripts.
334 Variant
<ScriptTextBuffer
<char16_t
>, ScriptTextBuffer
<Utf8Unit
>>>
337 // The length of script source text, set when reading completes. This is used
338 // since mScriptData is cleared when the source is passed to the JS engine.
339 size_t mScriptTextLength
;
341 // Holds the SRI serialized hash and the script bytecode for non-inline
342 // scripts. The data is laid out according to ScriptBytecodeDataLayout
343 // or, if compression is enabled, ScriptBytecodeCompressedDataLayout.
344 mozilla::Vector
<uint8_t> mScriptBytecode
;
345 uint32_t mBytecodeOffset
; // Offset of the bytecode in mScriptBytecode
347 const nsCOMPtr
<nsIURI
> mURI
;
348 nsCOMPtr
<nsIPrincipal
> mOriginPrincipal
;
350 // Keep the URI's filename alive during off thread parsing.
351 // Also used by workers to report on errors while loading, and used by
352 // worklets as the file name in compile options.
355 // The base URL used for resolving relative module imports.
356 nsCOMPtr
<nsIURI
> mBaseURL
;
358 // Holds the top-level JSScript that corresponds to the current source, once
359 // it is parsed, and planned to be saved in the bytecode cache.
361 // NOTE: This field is not used for ModuleLoadRequest.
362 // See ModuleLoadRequest::mIsMarkedForBytecodeEncoding.
363 JS::Heap
<JSScript
*> mScriptForBytecodeEncoding
;
365 // Holds the Cache information, which is used to register the bytecode
366 // on the cache entry, such that we can load it the next time.
367 nsCOMPtr
<nsICacheInfoChannel
> mCacheInfo
;
369 // LoadContext for augmenting the load depending on the loading
370 // context (DOM, Worker, etc.)
371 RefPtr
<LoadContextBase
> mLoadContext
;
374 class ScriptLoadRequestList
: private mozilla::LinkedList
<ScriptLoadRequest
> {
375 using super
= mozilla::LinkedList
<ScriptLoadRequest
>;
378 ~ScriptLoadRequestList();
380 void CancelRequestsAndClear();
383 bool Contains(ScriptLoadRequest
* aElem
) const;
386 using super::getFirst
;
387 using super::isEmpty
;
389 void AppendElement(ScriptLoadRequest
* aElem
) {
390 MOZ_ASSERT(!aElem
->isInList());
395 already_AddRefed
<ScriptLoadRequest
> Steal(ScriptLoadRequest
* aElem
) {
396 aElem
->removeFrom(*this);
397 return dont_AddRef(aElem
);
400 already_AddRefed
<ScriptLoadRequest
> StealFirst() {
401 MOZ_ASSERT(!isEmpty());
402 return Steal(getFirst());
405 void Remove(ScriptLoadRequest
* aElem
) {
406 aElem
->removeFrom(*this);
411 inline void ImplCycleCollectionUnlink(ScriptLoadRequestList
& aField
) {
412 while (!aField
.isEmpty()) {
413 RefPtr
<ScriptLoadRequest
> first
= aField
.StealFirst();
417 inline void ImplCycleCollectionTraverse(
418 nsCycleCollectionTraversalCallback
& aCallback
,
419 ScriptLoadRequestList
& aField
, const char* aName
, uint32_t aFlags
) {
420 for (ScriptLoadRequest
* request
= aField
.getFirst(); request
;
421 request
= request
->getNext()) {
422 CycleCollectionNoteChild(aCallback
, request
, aName
, aFlags
);
426 } // namespace loader
429 #endif // js_loader_ScriptLoadRequest_h