Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / js / loader / ScriptLoadRequest.h
blobd4bb030aef34b0e36689e54e3b6b73f51969a4df
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;
40 enum class RequestPriority : uint8_t;
42 } // namespace mozilla::dom
44 namespace mozilla::loader {
45 class ComponentLoadContext;
46 } // namespace mozilla::loader
48 namespace JS {
49 namespace 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 {
61 NotParserInserted,
62 ParserInserted,
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:
69 * integrity metadata
70 * The integrity metadata used for the initial fetch. This is
71 * implemented in ScriptLoadRequest, as it changes for every
72 * ScriptLoadRequest.
74 * referrerPolicy
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
78 * ScriptLoadRequest.
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();
89 public:
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
102 * classic scripts)
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
119 * imported modules
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;
140 * ScriptLoadRequest
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;
177 protected:
178 virtual ~ScriptLoadRequest();
180 public:
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 {
219 Fetching,
220 Compiling,
221 LoadingImports,
222 Ready,
223 Canceled
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
232 // otherwise.
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;
246 mScriptData.reset();
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
257 // time.
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; }
325 void SetBytecode();
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
344 // script.
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
351 // imported modules
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.
360 mozilla::Maybe<
361 Variant<ScriptTextBuffer<char16_t>, ScriptTextBuffer<Utf8Unit>>>
362 mScriptData;
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.
380 nsAutoCString mURL;
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
402 // preload.
403 uint64_t mEarlyHintPreloaderId;
406 class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> {
407 using super = mozilla::LinkedList<ScriptLoadRequest>;
409 public:
410 ~ScriptLoadRequestList();
412 void CancelRequestsAndClear();
414 #ifdef DEBUG
415 bool Contains(ScriptLoadRequest* aElem) const;
416 #endif // DEBUG
418 using super::getFirst;
419 using super::isEmpty;
421 void AppendElement(ScriptLoadRequest* aElem) {
422 MOZ_ASSERT(!aElem->isInList());
423 NS_ADDREF(aElem);
424 insertBack(aElem);
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);
439 NS_RELEASE(aElem);
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
459 } // namespace JS
461 #endif // js_loader_ScriptLoadRequest_h