Bug 1838629 - Part 6: Add a nursery pointer to RootingContext r=sfink
[gecko.git] / js / loader / ScriptLoadRequest.h
blob2624c3f25e96db1853d7dc37313e6b545de9bdc8
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"
27 #include "nsCOMPtr.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
47 namespace JS {
48 class OffThreadToken;
50 namespace 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:
62 * cryptographic nonce
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.
69 * parser metadata
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.
74 * integrity metadata
75 * The integrity metadata used for the initial fetch. This is
76 * implemented in ScriptLoadRequest, as it changes for every
77 * ScriptLoadRequest.
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();
88 public:
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
100 * classic scripts)
102 const mozilla::CORSMode mCORSMode;
105 * The referrer policy used for the initial fetch and for fetching any
106 * imported modules
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;
127 * ScriptLoadRequest
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;
164 protected:
165 virtual ~ScriptLoadRequest();
167 public:
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 {
205 Fetching,
206 Compiling,
207 LoadingImports,
208 Ready,
209 Canceled
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;
231 mScriptData.reset();
234 bool IsUTF8ParsingEnabled();
236 void SetTextSource() {
237 MOZ_ASSERT(IsUnknownDataType());
238 mDataType = DataType::eTextSource;
239 if (IsUTF8ParsingEnabled()) {
240 mScriptData.emplace(VariantType<ScriptTextBuffer<Utf8Unit>>());
241 } else {
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
248 // time.
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; }
302 void SetBytecode();
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
321 // script.
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.
333 mozilla::Maybe<
334 Variant<ScriptTextBuffer<char16_t>, ScriptTextBuffer<Utf8Unit>>>
335 mScriptData;
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.
353 nsAutoCString mURL;
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>;
377 public:
378 ~ScriptLoadRequestList();
380 void CancelRequestsAndClear();
382 #ifdef DEBUG
383 bool Contains(ScriptLoadRequest* aElem) const;
384 #endif // DEBUG
386 using super::getFirst;
387 using super::isEmpty;
389 void AppendElement(ScriptLoadRequest* aElem) {
390 MOZ_ASSERT(!aElem->isInList());
391 NS_ADDREF(aElem);
392 insertBack(aElem);
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);
407 NS_RELEASE(aElem);
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
427 } // namespace JS
429 #endif // js_loader_ScriptLoadRequest_h